diff --git a/AdSecCore/Functions/FindCrackLoadFunction.cs b/AdSecCore/Functions/FindCrackLoadFunction.cs index 4b6c37db..874570bb 100644 --- a/AdSecCore/Functions/FindCrackLoadFunction.cs +++ b/AdSecCore/Functions/FindCrackLoadFunction.cs @@ -167,7 +167,7 @@ public void Compute() { sls = solution.Solution.Serviceability.Check(baseLoad); SectionLoad.Value = sls.Load; - MaximumCracking.Value = sls.MaximumWidthCrack; + MaximumCracking.Value = new CrackLoad() { Load = sls.MaximumWidthCrack, Plane = solution.SectionDesign.LocalPlane }; } } diff --git a/AdSecCore/Functions/IFunction.cs b/AdSecCore/Functions/IFunction.cs index 95469d32..27516c97 100644 --- a/AdSecCore/Functions/IFunction.cs +++ b/AdSecCore/Functions/IFunction.cs @@ -15,7 +15,10 @@ public interface IFunction { } public abstract class Function : IFunction { + public List ErrorMessages { get; set; } = new List(); public List WarningMessages { get; set; } = new List(); + public List RemarkMessages { get; set; } = new List(); + public abstract FuncAttribute Metadata { get; set; } public abstract Organisation Organisation { get; set; } public virtual Attribute[] GetAllInputAttributes() { return Array.Empty(); } diff --git a/AdSecCore/Functions/ParametersGeneric.cs b/AdSecCore/Functions/ParametersGeneric.cs index 1f31bb3d..583535d8 100644 --- a/AdSecCore/Functions/ParametersGeneric.cs +++ b/AdSecCore/Functions/ParametersGeneric.cs @@ -1,4 +1,6 @@ -using Oasys.AdSec; +using System; + +using Oasys.AdSec; using Oasys.AdSec.DesignCode; using Oasys.AdSec.Mesh; using Oasys.Profiles; @@ -38,6 +40,11 @@ public class SectionSolution { public IServiceability Serviceability => Solution.Serviceability; } + public class CrackLoad { + public ICrack Load { get; set; } + public OasysPlane Plane { get; set; } = OasysPlane.PlaneYZ; + } + public class DoubleParameter : ParameterAttribute { } public class DoubleArrayParameter : BaseArrayParameter { } public class IntegerArrayParameter : BaseArrayParameter { } @@ -58,7 +65,12 @@ public class SubComponent { public class IntegerParameter : ParameterAttribute { } public class LoadParameter : ParameterAttribute { } - public class CrackParameter : ParameterAttribute { } + public class CrackParameter : ParameterAttribute { } + public class DeformationParameter : ParameterAttribute { } + public class GenericParameter : ParameterAttribute { } + public class CrackArrayParameter : BaseArrayParameter { } + public class SecantStiffnessParameter : ParameterAttribute { } + public class IntervalArrayParameter : BaseArrayParameter> { } } diff --git a/AdSecCore/Functions/SlsResultFunction.cs b/AdSecCore/Functions/SlsResultFunction.cs new file mode 100644 index 00000000..d48213ef --- /dev/null +++ b/AdSecCore/Functions/SlsResultFunction.cs @@ -0,0 +1,175 @@ + +using System; +using System.Collections.Generic; + +using AdSecGHCore.Constants; + +using Oasys.AdSec; + +using OasysUnits; +using OasysUnits.Units; + +namespace AdSecCore.Functions { + public class SlsResultFunction : Function { + + public SectionSolutionParameter SolutionInput { get; set; } = new SectionSolutionParameter { + Name = "Results", + NickName = "Res", + Description = "AdSec Results to perform serviceability check", + Access = Access.Item, + Optional = false, + }; + + public GenericParameter LoadInput { get; set; } = new GenericParameter { + Name = "Load", + NickName = "Ld", + Description = "AdSec Load (Load or Deformation) for which the strength results are to be calculated.", + Access = Access.Item, + Optional = false, + }; + + public LoadParameter LoadOutput { get; set; } = new LoadParameter { + Name = "Load", + NickName = "Ld", + Description = $"The section load under the applied action.{Environment.NewLine}If the applied deformation is outside the capacity range of the section, the returned load will be zero.", + Access = Access.Item, + Optional = false, + }; + + public CrackArrayParameter CrackOutput { get; set; } = new CrackArrayParameter { + Name = "Cracks", + NickName = "Crks", + Description = $"Crack results are calculated at bar positions or section surfaces depending on the Design Code specifications.{Environment.NewLine}If the applied action is outside the capacity range of the section, the returned list will be empty. See MaximumCrack output for the crack result corresponding to the maximum crack width.", + Access = Access.List, + }; + + public CrackParameter MaximumCrackOutput { get; set; } = new CrackParameter { + Name = "MaximumCrack", + NickName = "MaxCrk", + Description = $"Crack results are calculated at bar positions or section surfaces depending on the Design Code specifications.{Environment.NewLine}If the applied action is outside the capacity range of the section, the returned list will be empty. See MaximumCrack output for the crack result corresponding to the maximum crack width.", + Access = Access.Item, + }; + + public DoubleParameter CrackUtilOutput { get; set; } = new DoubleParameter { + Name = "CrackUtil", + NickName = "Uc", + Description = $"The ratio of the applied load (moment and axial) to the load (moment and axial) in the same direction that would cause the section to crack. Ratio > 1 means section is cracked.{Environment.NewLine}The section is cracked when the cracking utilisation ratio is greater than 1. If the applied load is outside the capacity range of the section, the cracking utilisation will be maximum double value.", + Access = Access.Item, + }; + + public DeformationParameter DeformationOutput { get; set; } = new DeformationParameter { + Name = "Deformation", + NickName = "Def", + Description = "The section deformation under the applied action", + Access = Access.Item, + }; + + public SecantStiffnessParameter SecantStiffnessOutput { get; set; } = new SecantStiffnessParameter { + Name = "SecantStiffness", + NickName = "Es", + Description = "The secant stiffness under the applied action", + Access = Access.Item, + }; + + public IntervalArrayParameter UncrackedMomentRangesOutput { get; set; } = new IntervalArrayParameter { + Name = "Uncracked Moment Ranges", + NickName = "Mrs", + Description = "The range of moments", + Access = Access.List, + }; + + public override FuncAttribute Metadata { get; set; } = new FuncAttribute { + Name = "Find Crack Load", + NickName = "CrackLd", + Description = "Increases the load until set crack width is reached", + }; + + public override Organisation Organisation { get; set; } = new Organisation { + Category = CategoryName.Name(), + SubCategory = SubCategoryName.Cat7(), + }; + + + public override Attribute[] GetAllInputAttributes() { + return new Attribute[] { + SolutionInput, + LoadInput, + }; + + } + + public override Attribute[] GetAllOutputAttributes() { + return new Attribute[] { + LoadOutput, + CrackOutput, + MaximumCrackOutput, + CrackUtilOutput, + DeformationOutput, + SecantStiffnessOutput, + UncrackedMomentRangesOutput + }; + } + + public void DeformationDescription(StrainUnit strainUnit, CurvatureUnit curvatureUnit) { + var strainAbbreviation = Strain.GetAbbreviation(strainUnit); + var curvatureAbbreviation = $"{strainAbbreviation}{Curvature.GetAbbreviation(curvatureUnit)}"; + DeformationOutput.Description = $"The section deformation under the applied action. The output is a vector representing:{Environment.NewLine}X: Strain [{strainAbbreviation}]{Environment.NewLine}Y: Curvature around zz (so in local y-direction) [{curvatureAbbreviation}]{Environment.NewLine}Z: Curvature around yy (so in local z-direction) [{curvatureAbbreviation}]"; + } + + public void SecantStiffnessDescription(AxialStiffnessUnit axialUnit, BendingStiffnessUnit bendingUnit) { + SecantStiffnessOutput.Description = $"The secant stiffness under the applied action. The output is a vector representing:{Environment.NewLine}X: Axial stiffness [{AxialStiffness.GetAbbreviation(axialUnit)}],{Environment.NewLine}Y: The bending stiffness about the y-axis in the local coordinate system [{BendingStiffness.GetAbbreviation(bendingUnit)}],{Environment.NewLine}Z: The bending stiffness about the z-axis in the local coordinate system [{BendingStiffness.GetAbbreviation(bendingUnit)}]"; + } + + public void UncrackedMomentRangesDescription(MomentUnit momentUnit) { + UncrackedMomentRangesOutput.Description = $"The range of moments (in the direction of the applied moment, assuming constant axial force) over which the section remains uncracked. Moment values are in [{Moment.GetAbbreviation(momentUnit)}]"; + } + + public override void Compute() { + var momentUnit = ContextUnits.Instance.MomentUnit; + // get solution input + var solution = SolutionInput.Value; + IServiceabilityResult sls = null; + switch (LoadInput.Value) { + case ILoad load: + sls = solution.Serviceability.Check(load); + break; + case IDeformation deformation: + sls = solution.Serviceability.Check(deformation); + break; + default: + ErrorMessages.Add("Invalid Load Input"); + return; + } + + LoadOutput.Value = sls.Load; + DeformationOutput.Value = sls.Deformation; + + + var cracks = new List(); + foreach (var crack in sls.Cracks) { + cracks.Add(new CrackLoad() { Load = crack, Plane = solution.SectionDesign.LocalPlane }); + } + CrackOutput.Value = cracks.ToArray(); + + MaximumCrackOutput.Value = new CrackLoad() { Load = sls.MaximumWidthCrack, Plane = solution.SectionDesign.LocalPlane }; + CrackUtilOutput.Value = sls.CrackingUtilisation.As(RatioUnit.DecimalFraction); + if (CrackUtilOutput.Value > 1) { + if (CrackOutput.Value.Length == 0) { + WarningMessages.Add("The section is failing and the cracks are so large we can't even compute them!"); + } else { + RemarkMessages.Add("The section is cracked"); + } + } + DeformationOutput.Value = sls.Deformation; + SecantStiffnessOutput.Value = sls.SecantStiffness; + + var momentRanges = new List>(); + foreach (var range in sls.UncrackedMomentRanges) { + var interval = new Tuple(range.Min.As(momentUnit), range.Max.As(momentUnit)); + momentRanges.Add(interval); + } + UncrackedMomentRangesOutput.Value = momentRanges.ToArray(); + } + } + +} diff --git a/AdSecCoreTests/SlsResultFunctionTests.cs b/AdSecCoreTests/SlsResultFunctionTests.cs new file mode 100644 index 00000000..c887a237 --- /dev/null +++ b/AdSecCoreTests/SlsResultFunctionTests.cs @@ -0,0 +1,100 @@ +using AdSecCore; +using AdSecCore.Builders; +using AdSecCore.Functions; + +using Oasys.AdSec; + +using OasysUnits; +using OasysUnits.Units; + +namespace AdSecCoreTests.Functions { + public class SlsResultFunctionTest { + private readonly SlsResultFunction _component; + private static SectionSolution? Solution { get; set; } = null; + public SlsResultFunctionTest() { + _component = new SlsResultFunction(); + if (Solution == null) { + Solution = new SolutionBuilder().Build(); + } + _component.SolutionInput.Value = Solution; + _component.LoadInput.Value = ILoad.Create(Force.FromKilonewtons(100), Moment.FromKilonewtonMeters(100), Moment.Zero); + } + + [Fact] + public void ShouldHaveCorrectMetadata() { + Assert.Equal("Find Crack Load", _component.Metadata.Name); + Assert.Equal("CrackLd", _component.Metadata.NickName); + Assert.Equal("Increases the load until set crack width is reached", _component.Metadata.Description); + } + + + [Fact] + public void ShouldHaveCorrectInputAttributes() { + var inputs = _component.GetAllInputAttributes(); + Assert.Equal(2, inputs.Length); + Assert.IsType(inputs[0]); + Assert.IsType(inputs[1]); + } + + [Fact] + public void ShouldHaveCorrectOutputAttributes() { + var outputs = _component.GetAllOutputAttributes(); + Assert.Equal(7, outputs.Length); + Assert.IsType(outputs[0]); + Assert.IsType(outputs[1]); + Assert.IsType(outputs[2]); + Assert.IsType(outputs[3]); + Assert.IsType(outputs[4]); + Assert.IsType(outputs[5]); + Assert.IsType(outputs[6]); + } + + [Fact] + public void ShoulHaveValidDeformationDescription() { + _component.DeformationDescription(StrainUnit.Ratio, CurvatureUnit.PerMeter); + var description = _component.DeformationOutput.Description; + Assert.Contains("[εm⁻¹]", description); + Assert.Contains("[εm⁻¹]", description); + } + + [Fact] + public void ShouldHaveValidSecantStiffnessDescription() { + _component.SecantStiffnessDescription(AxialStiffnessUnit.Newton, BendingStiffnessUnit.NewtonSquareMeter); + var description = _component.SecantStiffnessOutput.Description; + Assert.Contains("[N]", description); + Assert.Contains("[N·m²]", description); + Assert.Contains("[N·m²]", description); + } + + [Fact] + public void ShouldHaveValidUncrackedMomentRangesDescription() { + _component.UncrackedMomentRangesDescription(MomentUnit.NewtonMeter); + Assert.Contains("[N·m]", _component.UncrackedMomentRangesOutput.Description); + } + + [Fact] + public void ShouldComputeCorrectly() { + _component.Compute(); + var expectedLoad = ILoad.Create(Force.FromKilonewtons(100), Moment.FromKilonewtonMeters(100), Moment.Zero); + var expectedDeformation = IDeformation.Create(Strain.FromRatio(0.0014), Curvature.FromPerMeters(0.0064), Curvature.FromPerMeters(0.0029)); + Assert.True(IsLoadEqual(expectedLoad, _component.LoadOutput.Value)); + Assert.True(IsDeformationEqual(expectedDeformation, _component.DeformationOutput.Value)); + Assert.Equal(0.00208, _component.MaximumCrackOutput.Value.Load.Width.Value, new DoubleComparer()); + Assert.Equal(5.8156, _component.CrackUtilOutput.Value, new DoubleComparer()); + Assert.Single(_component.RemarkMessages); + Assert.Equal(69, _component.CrackOutput.Value.Length); + } + + private static bool IsLoadEqual(ILoad expected, ILoad calculated) { + return expected.X.Value.Equals(calculated.X.Value) && expected.YY.Value.Equals(calculated.YY.Value) && expected.ZZ.Value.Equals(calculated.ZZ.Value); + } + + private static bool IsDeformationEqual(IDeformation expected, IDeformation calculated) { + var tolernaceStrain = Strain.FromRatio(0.00001); + var tolernaceCurvature = Curvature.FromPerMeters(0.0001); + return expected.X.Equals(calculated.X, tolernaceStrain) && expected.YY.Equals(calculated.YY, tolernaceCurvature) && expected.ZZ.Equals(calculated.ZZ, tolernaceCurvature); + } + + + } +} diff --git a/AdSecGH/Components/6_Results/ResultsSLS.cs b/AdSecGH/Components/6_Results/ResultsSLS.cs index ab206711..bc90221c 100644 --- a/AdSecGH/Components/6_Results/ResultsSLS.cs +++ b/AdSecGH/Components/6_Results/ResultsSLS.cs @@ -3,146 +3,40 @@ using System.Drawing; using System.Linq; -using AdSecGH.Helpers; -using AdSecGH.Parameters; +using AdSecCore.Functions; + using AdSecGH.Properties; using AdSecGHCore.Constants; using Grasshopper.Kernel; -using Grasshopper.Kernel.Types; +using Grasshopper.Kernel.Geometry; -using Oasys.AdSec; +using Oasys.GH.Helpers; using OasysGH; -using OasysGH.Components; using OasysGH.Units; -using OasysUnits; -using OasysUnits.Units; - -using Rhino.Geometry; - namespace AdSecGH.Components { - public class ResultsSLS : GH_OasysComponent { + public class SlsResultGh : SlsResultFunction { + public SlsResultGh() { + + } + } - public ResultsSLS() : base("Serviceability Result", "SLS", - "Performs serviceability analysis (SLS), for a given Load or Deformation.", CategoryName.Name(), - SubCategoryName.Cat7()) { - Hidden = false; // sets the initial state of the component to hidden + public class SlsResult : ComponentAdapter { + protected override void BeforeSolveInstance() { + BusinessComponent.DeformationDescription(DefaultUnits.StrainUnitResult, DefaultUnits.CurvatureUnit); + BusinessComponent.SecantStiffnessDescription(DefaultUnits.AxialStiffnessUnit, DefaultUnits.BendingStiffnessUnit); + BusinessComponent.UncrackedMomentRangesDescription(DefaultUnits.MomentUnit); + RefreshOutputParameter(BusinessComponent.GetAllOutputAttributes()); } - // This region handles how the component in displayed on the ribbon including name, exposure level and icon + public SlsResult() { Hidden = true; Category = CategoryName.Name(); SubCategory = SubCategoryName.Cat7(); } public override Guid ComponentGuid => new Guid("27ba3ec5-b94c-43ad-8623-087540413628"); public override GH_Exposure Exposure => GH_Exposure.primary; public override OasysPluginInfo PluginInfo => AdSecGH.PluginInfo.Instance; protected override Bitmap Icon => Resources.SLS; - - protected override void RegisterInputParams(GH_InputParamManager pManager) { - pManager.AddGenericParameter("Results", "Res", "AdSec Results to perform serviceability check on.", - GH_ParamAccess.item); - pManager.AddGenericParameter("Load", "Ld", - "AdSec Load (Load or Deformation) for which the strength results are to be calculated.", GH_ParamAccess.item); - } - - protected override void RegisterOutputParams(GH_OutputParamManager pManager) { - string strainUnitAbbreviation = Strain.GetAbbreviation(DefaultUnits.StrainUnitResult); - IQuantity curvature = new Curvature(0, DefaultUnits.CurvatureUnit); - string curvatureUnitAbbreviation = string.Concat(curvature.ToString().Where(char.IsLetter)); - IQuantity axial = new AxialStiffness(0, DefaultUnits.AxialStiffnessUnit); - string axialUnitAbbreviation = string.Concat(axial.ToString().Where(char.IsLetter)); - IQuantity bending = new BendingStiffness(0, DefaultUnits.BendingStiffnessUnit); - string bendingUnitAbbreviation = string.Concat(bending.ToString().Where(char.IsLetter)); - IQuantity moment = new Moment(0, DefaultUnits.MomentUnit); - string momentUnitAbbreviation = string.Concat(moment.ToString().Where(char.IsLetter)); - - pManager.AddGenericParameter("Load", "Ld", - $"The section load under the applied action.{Environment.NewLine}If the applied deformation is outside the capacity range of the section, the returned load will be zero.", - GH_ParamAccess.item); - pManager.AddGenericParameter("Cracks", "Cks", - $"Crack results are calculated at bar positions or section surfaces depending on the Design Code specifications.{Environment.NewLine}If the applied action is outside the capacity range of the section, the returned list will be empty. See MaximumCrack output for the crack result corresponding to the maximum crack width.", GH_ParamAccess.item); - - pManager.AddGenericParameter("MaximumCrack", "Crk", - $"The crack result from Cracks that corresponds to the maximum crack width.{Environment.NewLine}If the applied action is outside the capacity range of the section, the returned maximum width crack result will be maximum double value.", GH_ParamAccess.item); - - pManager.AddNumberParameter("CrackUtil", "Uc", - $"The ratio of the applied load (moment and axial) to the load (moment and axial) in the same direction that would cause the section to crack. Ratio > 1 means section is cracked.{Environment.NewLine}The section is cracked when the cracking utilisation ratio is greater than 1. If the applied load is outside the capacity range of the section, the cracking utilisation will be maximum double value.", GH_ParamAccess.item); - - pManager.AddVectorParameter("Deformation", "Def", - $"The section deformation under the applied action. The output is a vector representing:{Environment.NewLine}X: Strain [{strainUnitAbbreviation}],{Environment.NewLine}Y: Curvature around zz (so in local y-direction) [{curvatureUnitAbbreviation}],{Environment.NewLine}Z: Curvature around yy (so in local z-direction) [{curvatureUnitAbbreviation}]", GH_ParamAccess.item); - - pManager.AddVectorParameter("SecantStiffness", "Es", - $"The secant stiffness under the applied action. The output is a vector representing:{Environment.NewLine}X: Axial stiffness [{axialUnitAbbreviation}],{Environment.NewLine}Y: The bending stiffness about the y-axis in the local coordinate system [{bendingUnitAbbreviation}],{Environment.NewLine}Z: The bending stiffness about the z-axis in the local coordinate system [{bendingUnitAbbreviation}]", GH_ParamAccess.item); - - pManager.AddIntervalParameter("Uncracked Moment Ranges", "MRs", - $"The range of moments (in the direction of the applied moment, assuming constant axial force) over which the section remains uncracked. Moment values are in [{momentUnitAbbreviation}]", - GH_ParamAccess.list); - } - - protected override void SolveInstance(IGH_DataAccess DA) { - // get solution input - var solution = this.GetSolutionGoo(DA, 0); - - IServiceabilityResult sls = null; - - // get load - can be either load or deformation - var gh_typ = new GH_ObjectWrapper(); - if (DA.GetData(1, ref gh_typ)) { - // try cast directly to quantity type - if (gh_typ.Value is AdSecLoadGoo load) { - sls = solution.Value.Serviceability.Check(load.Value); - } else if (gh_typ.Value is AdSecDeformationGoo def) { - sls = solution.Value.Serviceability.Check(def.Value); - } else { - AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Unable to convert {Params.Input[1].NickName} to AdSec Load"); - return; - } - } else { - AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, - $"Input parameter {Params.Input[1].NickName} failed to collect data!"); - return; - } - - DA.SetData(0, new AdSecLoadGoo(sls.Load, solution.LocalPlane)); - - var cracks = new List(); - foreach (var crack in sls.Cracks) { - cracks.Add(new AdSecCrackGoo(crack, solution.LocalPlane)); - } - - DA.SetDataList(1, cracks); - - if (sls.MaximumWidthCrack != null && sls.MaximumWidthCrack.Width.Meters < 1) { - DA.SetData(2, new AdSecCrackGoo(sls.MaximumWidthCrack, solution.LocalPlane)); - } - - double util = sls.CrackingUtilisation.As(RatioUnit.DecimalFraction); - DA.SetData(3, util); - if (util > 1) { - if (cracks.Count == 0) { - AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, - "The section is failing and the cracks are so large we can't even compute them!"); - } else { - AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "The section is cracked"); - } - } - - DA.SetData(4, - new Vector3d(sls.Deformation.X.As(DefaultUnits.StrainUnitResult), - sls.Deformation.YY.As(DefaultUnits.CurvatureUnit), sls.Deformation.ZZ.As(DefaultUnits.CurvatureUnit))); - - DA.SetData(5, - new Vector3d(sls.SecantStiffness.X.As(DefaultUnits.AxialStiffnessUnit), - sls.SecantStiffness.YY.As(DefaultUnits.BendingStiffnessUnit), - sls.SecantStiffness.ZZ.As(DefaultUnits.BendingStiffnessUnit))); - - var momentRanges = new List(); - foreach (var mrng in sls.UncrackedMomentRanges) { - var interval = new Interval(mrng.Min.As(DefaultUnits.MomentUnit), mrng.Max.As(DefaultUnits.MomentUnit)); - momentRanges.Add(new GH_Interval(interval)); - } - - DA.SetDataList(6, momentRanges); - } } + } diff --git a/AdSecGH/Helpers/BusinessExtensions.cs b/AdSecGH/Helpers/BusinessExtensions.cs index c191b030..573bbb6e 100644 --- a/AdSecGH/Helpers/BusinessExtensions.cs +++ b/AdSecGH/Helpers/BusinessExtensions.cs @@ -131,7 +131,32 @@ private static readonly Dictionary> ToGhParam a => new Param_Integer { Name = a.Name, NickName = a.NickName, Description = a.Description, Access = GetAccess(a), } - }, + }, { + typeof(DeformationParameter), + a => new Param_GenericObject { + Name = a.Name, NickName = a.NickName, Description = a.Description, Access = GetAccess(a), + } + },{ + typeof(GenericParameter), + a => new Param_GenericObject { + Name = a.Name, NickName = a.NickName, Description = a.Description, Access = GetAccess(a), + } + },{ + typeof(CrackArrayParameter), + a => new Param_GenericObject { + Name = a.Name, NickName = a.NickName, Description = a.Description, Access = GetAccess(a), + } + },{ + typeof(SecantStiffnessParameter), + a => new Param_GenericObject { + Name = a.Name, NickName = a.NickName, Description = a.Description, Access = GetAccess(a), + } + },{ + typeof(IntervalArrayParameter), + a => new Param_GenericObject { + Name = a.Name, NickName = a.NickName, Description = a.Description, Access = GetAccess(a), + } + } }; private static readonly Dictionary> ToGoo @@ -179,7 +204,34 @@ private static readonly Dictionary> ToGoo var load = (a as LoadParameter).Value; return new AdSecLoadGoo(load); } - }, + },{ typeof(IntervalArrayParameter), a => { + var intervals = (a as IntervalArrayParameter).Value; + var ranges = new List(); + foreach (var interval in intervals) { + ranges.Add(new GH_Interval(new Interval(interval.Item1, interval.Item2))); + } + return ranges; + } + },{ typeof(SecantStiffnessParameter), a => { + var stiffness = (a as SecantStiffnessParameter).Value; + return new Vector3d(stiffness.X.As(DefaultUnits.AxialStiffnessUnit), + stiffness.YY.As(DefaultUnits.BendingStiffnessUnit), + stiffness.ZZ.As(DefaultUnits.BendingStiffnessUnit)); + } + } + ,{ typeof(CrackArrayParameter), a => { + var cracks = (a as CrackArrayParameter).Value; + var cracksGoo = new List(); + foreach (var crack in cracks) { + cracksGoo.Add(new AdSecCrackGoo(crack)); + } + return cracksGoo; + } + },{ typeof(DeformationParameter), a => { + var deformation = (a as DeformationParameter).Value; + return new AdSecDeformationGoo(deformation); + } + } }; private static readonly Dictionary> GooToParam diff --git a/AdSecGH/Helpers/ComponentAdapter.cs b/AdSecGH/Helpers/ComponentAdapter.cs index 0d17c775..480791b3 100644 --- a/AdSecGH/Helpers/ComponentAdapter.cs +++ b/AdSecGH/Helpers/ComponentAdapter.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using AdSecCore.Functions; @@ -7,6 +8,7 @@ using OasysGH; using OasysGH.Components; +using Attribute = AdSecCore.Functions.Attribute; namespace Oasys.GH.Helpers { public abstract class ComponentAdapter : GH_OasysComponent, IDefaultValues where T : IFunction { @@ -37,10 +39,26 @@ protected override void SolveInstance(IGH_DataAccess DA) { foreach (var warning in function.WarningMessages) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, warning); } + + foreach (var remark in function.RemarkMessages) { + AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, remark); + } + + foreach (var error in function.ErrorMessages) { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, error); + } + + if (function.ErrorMessages.Count > 0) { return; } } BusinessComponent.SetOutputValues(this, DA); } + + public void RefreshOutputParameter(Attribute[] attributes) { + for (int id = 0; id < attributes.Length; id++) { + Params.Output[id].Description = attributes[id].Description; + } + } } public interface IDefaultValues { diff --git a/AdSecGH/Parameters/AdSecCrackGoo.cs b/AdSecGH/Parameters/AdSecCrackGoo.cs index 02e2146c..2f82670a 100644 --- a/AdSecGH/Parameters/AdSecCrackGoo.cs +++ b/AdSecGH/Parameters/AdSecCrackGoo.cs @@ -1,19 +1,20 @@ using System; using System.Drawing; +using AdSecCore.Functions; + +using AdSecGH.Helpers; + using Grasshopper; using Grasshopper.Kernel; using Grasshopper.Kernel.Types; -using Oasys.AdSec; - using OasysGH; using OasysGH.Parameters; using Rhino.Geometry; - namespace AdSecGH.Parameters { - public class AdSecCrackGoo : GH_OasysGeometricGoo, IGH_PreviewData { + public class AdSecCrackGoo : GH_OasysGeometricGoo, IGH_PreviewData { public static string Description => "AdSec Crack Parameter"; public static string Name => "Crack"; public static string NickName => "Cr"; @@ -32,34 +33,28 @@ public override BoundingBox Boundingbox { public override BoundingBox ClippingBox => Boundingbox; public override OasysPluginInfo PluginInfo => AdSecGH.PluginInfo.Instance; private Line m_line; - private Plane m_plane; - private Point3d m_point = Point3d.Unset; - - public AdSecCrackGoo(ICrack item) : base(item) { - } - - public AdSecCrackGoo(ICrack crack, Plane local) : base(crack) { - m_value = crack; - m_plane = local; + private Point3d m_point; + public AdSecCrackGoo(CrackLoad crackLoad) : base(crackLoad) { + var plane = Value.Plane.ToGh(); // create point from crack position in global axis var point = new Point3d( - crack.Position.Y.Value, - crack.Position.Z.Value, + m_value.Load.Position.Y.Value, + m_value.Load.Position.Z.Value, 0); // remap to local coordinate system - var mapFromLocal = Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, local); + var mapFromLocal = Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, plane); point.Transform(mapFromLocal); m_point = point; // move starting point of line by half the width - var halfCrack = new Vector3d(local.ZAxis); + var halfCrack = new Vector3d(plane.ZAxis); halfCrack.Unitize(); halfCrack = new Vector3d( - halfCrack.X * crack.Width.Value / 2, - halfCrack.Y * crack.Width.Value / 2, - halfCrack.Z * crack.Width.Value / 2); + halfCrack.X * m_value.Load.Width.Value / 2, + halfCrack.Y * m_value.Load.Width.Value / 2, + halfCrack.Z * m_value.Load.Width.Value / 2); var move = Rhino.Geometry.Transform.Translation(halfCrack); var crackStart = new Point3d(m_point); @@ -69,13 +64,12 @@ public AdSecCrackGoo(ICrack crack, Plane local) : base(crack) { var crackWidth = new Vector3d(halfCrack); crackWidth.Unitize(); crackWidth = new Vector3d( - crackWidth.X * crack.Width.Value * -1, - crackWidth.Y * crack.Width.Value * -1, - crackWidth.Z * crack.Width.Value * -1); + crackWidth.X * m_value.Load.Width.Value * -1, + crackWidth.Y * m_value.Load.Width.Value * -1, + crackWidth.Z * m_value.Load.Width.Value * -1); m_line = new Line(crackStart, crackWidth); } - public override bool CastFrom(object source) { if (source == null) { return false; @@ -85,7 +79,7 @@ public override bool CastFrom(object source) { public override bool CastTo(out Q target) { if (typeof(Q).IsAssignableFrom(typeof(AdSecCrackGoo))) { - target = (Q)(object)new AdSecCrackGoo(Value, m_plane); + target = (Q)(object)new AdSecCrackGoo(Value); return true; } @@ -100,12 +94,12 @@ public override bool CastTo(out Q target) { } if (typeof(Q).IsAssignableFrom(typeof(Vector3d))) { - target = (Q)(object)new Vector3d(Value.Width.Value, m_point.Y, m_point.Z); + target = (Q)(object)new Vector3d(Value.Load.Width.Value, m_point.Y, m_point.Z); return true; } if (typeof(Q).IsAssignableFrom(typeof(GH_Vector))) { - target = (Q)(object)new GH_Vector(new Vector3d(Value.Width.Value, m_point.Y, m_point.Z)); + target = (Q)(object)new GH_Vector(new Vector3d(Value.Load.Width.Value, m_point.Y, m_point.Z)); return true; } @@ -120,12 +114,12 @@ public override bool CastTo(out Q target) { } if (typeof(Q).IsAssignableFrom(typeof(GH_UnitNumber))) { - target = (Q)(object)new GH_UnitNumber(Value.Width); + target = (Q)(object)new GH_UnitNumber(Value.Load.Width); return true; } if (typeof(Q).IsAssignableFrom(typeof(GH_Number))) { - target = (Q)(object)new GH_Number(Value.Width.Value); + target = (Q)(object)new GH_Number(Value.Load.Width.Value); return true; } @@ -153,7 +147,7 @@ public override IGH_GeometricGoo Duplicate() { } public override IGH_GeometricGoo DuplicateGeometry() { - var dup = new AdSecCrackGoo(Value, m_plane); + var dup = new AdSecCrackGoo(Value); return dup; } @@ -178,7 +172,7 @@ public override IGH_GeometricGoo Morph(SpaceMorph xmorph) { public override string ToString() { return - $"AdSec {TypeName} {{Y:{Math.Round(Value.Position.Y.Value, 4)}{Value.Position.Y.Unit}, Z:{Math.Round(Value.Position.Z.Value, 4)}{Value.Position.Z.Unit}, Width:{Math.Round(Value.Width.Value, 4)}{Value.Width.Unit}}}"; + $"AdSec {TypeName} {{Y:{Math.Round(Value.Load.Position.Y.Value, 4)}{Value.Load.Position.Y.Unit}, Z:{Math.Round(Value.Load.Position.Z.Value, 4)}{Value.Load.Position.Z.Unit}, Width:{Math.Round(Value.Load.Width.Value, 4)}{Value.Load.Width.Unit}}}"; } public override IGH_GeometricGoo Transform(Transform xform) { diff --git a/AdSecGHTests/Components/6_Results/SlsResultTests.cs b/AdSecGHTests/Components/6_Results/SlsResultTests.cs new file mode 100644 index 00000000..d8db7363 --- /dev/null +++ b/AdSecGHTests/Components/6_Results/SlsResultTests.cs @@ -0,0 +1,92 @@ +using System; +using System.ComponentModel; + +using AdSecCore.Builders; +using AdSecCore.Functions; + +using AdSecGH.Components; +using AdSecGH.Properties; + +using AdSecGHCore.Constants; + +using AdSecGHTests.Helpers; + +using Grasshopper.Kernel; + +using Oasys.AdSec; + +using OasysGH.Units; + +using OasysUnits; +using OasysUnits.Units; + +using Xunit; + +namespace AdSecGHTests.Components { + [Collection("GrasshopperFixture collection")] + public class SlsResultTests { + private readonly SlsResult _component; + private static SectionSolution Solution { get; set; } = null; + + public SlsResultTests() { + _component = new SlsResult(); + if (Solution == null) { + Solution = new SolutionBuilder().Build(); + } + ComponentTestHelper.SetInput(_component, Solution, 0); + } + + private void SetLoad(bool correctLoad = true) { + if (correctLoad) { + ComponentTestHelper.SetInput(_component, ILoad.Create(Force.FromKilonewtons(100), Moment.FromKilonewtonMeters(100), Moment.Zero), 1); + + } else { + ComponentTestHelper.SetInput(_component, string.Empty, 1); + } + ComponentTestHelper.ComputeData(_component); + } + + private void SetDeformation() { + ComponentTestHelper.SetInput(_component, IDeformation.Create(Strain.FromRatio(0.00001), Curvature.Zero, Curvature.Zero), 1); + } + + private void SetLargeLoad() { + ComponentTestHelper.SetInput(_component, ILoad.Create(Force.FromKilonewtons(-100), Moment.FromKilonewtonMeters(900), Moment.Zero), 1); + ComponentTestHelper.GetOutput(_component); + } + + [Fact] + public void ShouldHaveRemarkWhenUtiliztionIsGreaterThanOne() { + SetLoad(); + Assert.Single(_component.RuntimeMessages(GH_RuntimeMessageLevel.Remark)); + } + + [Fact] + public void ShouldRefreshComponent() { + var originalUnit = DefaultUnits.StrainUnitResult; + DefaultUnits.StrainUnitResult = StrainUnit.MicroStrain; + SetLoad(); + Assert.Contains("[µε]", _component.Params.Output[4].Description); + DefaultUnits.StrainUnitResult = originalUnit; + } + + [Fact] + public void ShouldHaveErrorForWrongLoad() { + SetLoad(false); + Assert.Single(_component.RuntimeMessages(GH_RuntimeMessageLevel.Error)); + } + + [Fact] + public void ShouldHaveWarningForHighLoad() { + SetLargeLoad(); + Assert.Single(_component.RuntimeMessages(GH_RuntimeMessageLevel.Warning)); + } + + [Fact] + public void ShouldCalculateCrackForGivenDeformation() { + SetDeformation(); + ComponentTestHelper.GetOutput(_component, 3); + Assert.NotNull(ComponentTestHelper.GetOutput(_component, 3)); + } + } +} diff --git a/AdSecGHTests/Helpers/ComponentTestHelper.cs b/AdSecGHTests/Helpers/ComponentTestHelper.cs index 113743df..87f61bbb 100644 --- a/AdSecGHTests/Helpers/ComponentTestHelper.cs +++ b/AdSecGHTests/Helpers/ComponentTestHelper.cs @@ -8,8 +8,7 @@ using Rhino.Geometry; namespace AdSecGHTests.Helpers { - public class ComponentTestHelper { - + public static class ComponentTestHelper { public static object GetOutput(GH_Component component, int index = 0, int branch = 0, int item = 0, bool forceUpdate = false) { if (forceUpdate || component.Params.Output[index].VolatileDataCount == 0) { component.ExpireSolution(true);