Repository: arup-group/AdSec-Grasshopper
Pull request #164: ADSECGH-123: Updated rebar stress/strain component
Author: login: SandeepArup
Created: 2025-04-25T07:55:31Z | Merged: 2025-05-02T11:11:01Z



Diff:
diff --git a/AdSecCore/Functions/ConcreteStressStrainFunction.cs b/AdSecCore/Functions/ConcreteStressStrainFunction.cs
index dbd1b867..7bdb0ea4 100644
--- a/AdSecCore/Functions/ConcreteStressStrainFunction.cs
+++ b/AdSecCore/Functions/ConcreteStressStrainFunction.cs
@@ -6,8 +6,8 @@
 
 namespace AdSecCore.Functions {
   public class ConcreteStressStrainFunction : ResultFunction {
-    private IServiceabilityResult Sls { get; set; }
-    private IStrengthResult Uls { get; set; }
+    protected IServiceabilityResult Sls { get; set; }
+    protected IStrengthResult Uls { get; set; }
 
     public PointParameter VertexInput { get; set; } = new PointParameter {
       Name = "Vertex Point",
@@ -56,6 +56,14 @@ public class ConcreteStressStrainFunction : ResultFunction {
       SubCategory = SubCategoryName.Cat7(),
     };
 
+    public override Attribute[] GetAllInputAttributes() {
+      return new Attribute[] {
+       SolutionInput,
+       LoadInput,
+       VertexInput,
+      };
+    }
+
     public override Attribute[] GetAllOutputAttributes() {
       return new Attribute[] {
        UlsStrainOutput,
@@ -67,16 +75,29 @@ public override Attribute[] GetAllOutputAttributes() {
 
     public override void UpdateOutputParameter() {
       base.UpdateOutputParameter();
-      string strainUnitAbbreviation = Strain.GetAbbreviation(StrainUnitResult);
-      string stressUnitAbbreviation = Pressure.GetAbbreviation(StressUnitResult);
-      UlsStrainOutput.Name = $"ULS Strain [{strainUnitAbbreviation}]";
-      UlsStressOutput.Name = $"ULS Stress [{stressUnitAbbreviation}]";
-      SlsStrainOutput.Name = $"SLS Strain [{strainUnitAbbreviation}]";
-      SlsStressOutput.Name = $"SLS Stress [{stressUnitAbbreviation}]";
+      UpdateOutputNames();
+    }
+
+    protected virtual void UpdateOutputNames() {
+      string strainUnit = GetStrainUnitResultAbbreviation();
+      string stressUnit = GetStressUnitResultAbbreviation();
+      UlsStrainOutput.Name = FormatStrainName("ULS", strainUnit);
+      UlsStressOutput.Name = FormatStressName("ULS", stressUnit);
+      SlsStrainOutput.Name = FormatStrainName("SLS", strainUnit);
+      SlsStressOutput.Name = FormatStressName("SLS", stressUnit);
+    }
+
+    protected virtual string FormatStrainName(string prefix, string unit) {
+      return $"{prefix} Strain [{unit}]";
+    }
+
+    protected virtual string FormatStressName(string prefix, string unit) {
+      return $"{prefix} Stress [{unit}]";
     }
 
 
     public override void Compute() {
+
       if (!ValidateInputs()) {
         return;
       }
@@ -86,18 +107,7 @@ public override void Compute() {
       ProcessOutput();
     }
 
-    protected override bool ValidateInputs() {
-      if (!base.ValidateInputs()) {
-        return false;
-      }
-      if (VertexInput.Value == null) {
-        ErrorMessages.Add("Vertex point is null");
-        return false;
-      }
-      return true;
-    }
-
-    private void ProcessInput() {
+    protected virtual void ProcessInput() {
       switch (LoadInput.Value) {
         case ILoad load:
           Uls = SolutionInput.Value.Solution.Strength.Check(load);
diff --git a/AdSecCore/Functions/IFunction.cs b/AdSecCore/Functions/IFunction.cs
index d48c9319..62111958 100644
--- a/AdSecCore/Functions/IFunction.cs
+++ b/AdSecCore/Functions/IFunction.cs
@@ -18,7 +18,7 @@ public interface IFunction {
   }
 
   public abstract class Function : IFunction {
-    public LengthUnit LengthUnit { get; set; } = LengthUnit.Meter;
+    public LengthUnit LengthUnitGeometry { get; set; } = LengthUnit.Meter;
     public LengthUnit LengthUnitResult { get; set; } = LengthUnit.Millimeter;
     public StrainUnit StrainUnitResult { get; set; } = StrainUnit.Ratio;
     public CurvatureUnit CurvatureUnit { get; set; } = CurvatureUnit.PerMeter;
@@ -30,6 +30,32 @@ public abstract class Function : IFunction {
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
 
+    public string GetStrainUnitResultAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
+    }
+    public string GetStressUnitResultAbbreviation() {
+      return Pressure.GetAbbreviation(StressUnitResult);
+    }
+    public string GetLengthUnitGeometryAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitGeometry);
+    }
+    public string GetLengthUnitResultAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitResult);
+    }
+
+    public virtual bool ValidateInputs() {
+      var inputs = GetAllInputAttributes();
+      foreach (var input in inputs) {
+        var valueProperty = input.GetType().GetProperty("Value");
+        var value = valueProperty.GetValue(input);
+        if (!input.Optional && value == null) {
+          ErrorMessages.Add($"{input.Name} input is null");
+          return false;
+        }
+      }
+      return true;
+    }
+
     public abstract FuncAttribute Metadata { get; set; }
     public abstract Organisation Organisation { get; set; }
     public virtual Attribute[] GetAllInputAttributes() { return Array.Empty<Attribute>(); }
diff --git a/AdSecCore/Functions/ParametersGeneric.cs b/AdSecCore/Functions/ParametersGeneric.cs
index 39925450..f3724aa3 100644
--- a/AdSecCore/Functions/ParametersGeneric.cs
+++ b/AdSecCore/Functions/ParametersGeneric.cs
@@ -146,4 +146,7 @@ public class GeometryParameter : ParameterAttribute<object> { }
   public class NeutralLineParameter : ParameterAttribute<NeutralAxis> { }
   public class StrainParameter : ParameterAttribute<Strain> { }
   public class PressureParameter : ParameterAttribute<Pressure> { }
+  public class StrainArrayParameter : BaseArrayParameter<Strain> { }
+  public class PressureArrayParameter : BaseArrayParameter<Pressure> { }
+
 }
diff --git a/AdSecCore/Functions/PointRebarFunction.cs b/AdSecCore/Functions/PointRebarFunction.cs
index c252e366..5c0e561e 100644
--- a/AdSecCore/Functions/PointRebarFunction.cs
+++ b/AdSecCore/Functions/PointRebarFunction.cs
@@ -49,7 +49,7 @@ public override Attribute[] GetAllOutputAttributes() {
     }
 
     public override void Compute() {
-      Point.Value = IPoint.Create(new Length(Y.Value, LengthUnit), new Length(Z.Value, LengthUnit));
+      Point.Value = IPoint.Create(new Length(Y.Value, LengthUnitGeometry), new Length(Z.Value, LengthUnitGeometry));
     }
   }
 }
diff --git a/AdSecCore/Functions/RebarGroupFunction.cs b/AdSecCore/Functions/RebarGroupFunction.cs
index 231c72d0..ac224916 100644
--- a/AdSecCore/Functions/RebarGroupFunction.cs
+++ b/AdSecCore/Functions/RebarGroupFunction.cs
@@ -183,7 +183,7 @@ public override void Compute() {
         }
 
         var rebarGroup = Layout.Value[i];
-        rebarGroup.Cover = ICover.Create(Length.From(coverSize, LengthUnit));
+        rebarGroup.Cover = ICover.Create(Length.From(coverSize, LengthUnitGeometry));
       }
     }
 
diff --git a/AdSecCore/Functions/RebarStressStrainFunction.cs b/AdSecCore/Functions/RebarStressStrainFunction.cs
new file mode 100644
index 00000000..82a33201
--- /dev/null
+++ b/AdSecCore/Functions/RebarStressStrainFunction.cs
@@ -0,0 +1,147 @@
+﻿using System.Collections.Generic;
+
+using AdSecGHCore.Constants;
+
+using Oasys.AdSec;
+using Oasys.AdSec.Reinforcement.Groups;
+using Oasys.Profiles;
+
+using OasysUnits;
+
+namespace AdSecCore.Functions {
+  public class RebarStressStrainFunction : ConcreteStressStrainFunction {
+    private ISection FlatSection { get; set; }
+    public List<IPoint> Points { get; private set; } = new List<IPoint>();
+    public List<Strain> StrainsULS { get; private set; } = new List<Strain>();
+    public List<Pressure> StressesULS { get; private set; } = new List<Pressure>();
+    public List<Strain> StrainsSLS { get; private set; } = new List<Strain>();
+    public List<Pressure> StressesSLS { get; private set; } = new List<Pressure>();
+
+    public PointArrayParameter PositionOutput { get; set; } = new PointArrayParameter {
+      Name = "Position",
+      NickName = "Vx",
+      Description = "Rebar positions as 2D vertices in the section's local yz-plane",
+      Access = Access.List,
+    };
+
+    public new StrainArrayParameter UlsStrainOutput { get; set; } = new StrainArrayParameter {
+      Name = "ULS Strain",
+      NickName = "εd",
+      Description = "ULS strain for each rebar position",
+      Access = Access.List,
+    };
+
+    public new PressureArrayParameter UlsStressOutput { get; set; } = new PressureArrayParameter {
+      Name = "ULS Stress",
+      NickName = "σd",
+      Description = "ULS stress for each rebar position",
+      Access = Access.List,
+    };
+
+    public new StrainArrayParameter SlsStrainOutput { get; set; } = new StrainArrayParameter {
+      Name = "SLS Strain",
+      NickName = "εk",
+      Description = "SLS strain for each rebar position",
+      Access = Access.List,
+    };
+
+    public new PressureArrayParameter SlsStressOutput { get; set; } = new PressureArrayParameter {
+      Name = "SLS Stress",
+      NickName = "σk",
+      Description = "SLS stress for each rebar position",
+      Access = Access.List,
+    };
+
+    public override FuncAttribute Metadata { get; set; } = new FuncAttribute {
+      Name = "Rebar Stress/Strain",
+      NickName = "RSS",
+      Description = "Calculate the Rebar Stress/Strains in the Section for a given Load or Deformation.",
+    };
+
+    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[] {
+        PositionOutput,
+        UlsStrainOutput,
+        UlsStressOutput,
+        SlsStrainOutput,
+        SlsStressOutput,
+      };
+    }
+
+    protected override void UpdateOutputNames() {
+      string strainUnit = GetStrainUnitResultAbbreviation();
+      string stressUnit = GetStressUnitResultAbbreviation();
+      string lengthUnit = GetLengthUnitResultAbbreviation();
+      UlsStrainOutput.Name = FormatStrainName("ULS", strainUnit);
+      UlsStressOutput.Name = FormatStressName("ULS", stressUnit);
+      SlsStrainOutput.Name = FormatStrainName("SLS", strainUnit);
+      SlsStressOutput.Name = FormatStressName("SLS", stressUnit);
+      PositionOutput.Name = FormatPositionName("Position", lengthUnit);
+    }
+
+    private static string FormatPositionName(string prefix, string unit) {
+      return $"{prefix} [{unit}]";
+    }
+
+    public override void Compute() {
+
+      if (!ValidateInputs()) {
+        return;
+      }
+
+      ProcessInput();
+      ProcessOutput();
+    }
+
+    private void ProcessOutput() {
+      ClearResults();
+
+      // Get flattened section
+      FlatSection = SolutionInput.Value.SectionDesign.FlattenSection();
+      foreach (var rebarGroup in FlatSection.ReinforcementGroups) {
+        if (rebarGroup is ISingleBars singleBars) {
+          foreach (var pos in singleBars.Positions) {
+            Points.Add(pos);
+
+            // Calculate ULS results
+            var strainULS = Uls.Deformation.StrainAt(pos);
+            StrainsULS.Add(strainULS);
+            StressesULS.Add(singleBars.BarBundle.Material.Strength.StressAt(strainULS));
+
+            // Calculate SLS results
+            var strainSLS = Sls.Deformation.StrainAt(pos);
+            StrainsSLS.Add(strainSLS);
+            StressesSLS.Add(singleBars.BarBundle.Material.Serviceability.StressAt(strainSLS));
+          }
+        }
+      }
+
+      // Set output parameters
+      PositionOutput.Value = Points.ToArray();
+      UlsStrainOutput.Value = StrainsULS.ToArray();
+      UlsStressOutput.Value = StressesULS.ToArray();
+      SlsStrainOutput.Value = StrainsSLS.ToArray();
+      SlsStressOutput.Value = StressesSLS.ToArray();
+    }
+
+    private void ClearResults() {
+      Points.Clear();
+      StrainsULS.Clear();
+      StressesULS.Clear();
+      StrainsSLS.Clear();
+      StressesSLS.Clear();
+    }
+  }
+}
diff --git a/AdSecCore/Functions/ResultFunction.cs b/AdSecCore/Functions/ResultFunction.cs
index 9808ebf4..62e0aa1b 100644
--- a/AdSecCore/Functions/ResultFunction.cs
+++ b/AdSecCore/Functions/ResultFunction.cs
@@ -164,19 +164,7 @@ public override void UpdateOutputParameter() {
       MomentRangesOutput.Description = UncrackedMomentRangesOutput.Description;
     }
 
-    protected virtual bool ValidateInputs() {
-      if (SolutionInput.Value == null) {
-        ErrorMessages.Add("Solution input is null");
-        return false;
-      }
-      if (LoadInput.Value == null) {
-        ErrorMessages.Add("Load input is null");
-        return false;
-      }
-      return IsLoadValid();
-    }
-
-    private bool IsLoadValid() {
+    protected virtual bool ValidateLoad() {
       switch (LoadInput.Value) {
         case ILoad load:
           if (!LoadExtensions.IsValid(load)) {
@@ -196,5 +184,12 @@ private bool IsLoadValid() {
       }
       return true;
     }
+
+    public override bool ValidateInputs() {
+      if (!base.ValidateInputs() || !ValidateLoad()) {
+        return false;
+      }
+      return true;
+    }
   }
 }
diff --git a/AdSecCoreTests/ConcreteStressStrainFunctionTests.cs b/AdSecCoreTests/ConcreteStressStrainFunctionTests.cs
index 0d2965f6..cd542577 100644
--- a/AdSecCoreTests/ConcreteStressStrainFunctionTests.cs
+++ b/AdSecCoreTests/ConcreteStressStrainFunctionTests.cs
@@ -51,6 +51,13 @@ public void ShouldHaveErrorMessageForNullSolution() {
       Assert.Single(_component.ErrorMessages);
     }
 
+    [Fact]
+    public void ShouldValidateOptionalVertex() {
+      _component.VertexInput.Value = null;
+      _component.VertexInput.Optional = true;
+      Assert.True(_component.ValidateInputs());
+    }
+
     [Fact]
     public void ShouldHaveErrorMessageForNullLoad() {
       _component.LoadInput.Value = null;
diff --git a/AdSecCoreTests/RebarStressStrainFunctionTests.cs b/AdSecCoreTests/RebarStressStrainFunctionTests.cs
new file mode 100644
index 00000000..4038beaf
--- /dev/null
+++ b/AdSecCoreTests/RebarStressStrainFunctionTests.cs
@@ -0,0 +1,117 @@
+﻿using AdSecCore;
+using AdSecCore.Builders;
+using AdSecCore.Functions;
+
+using Oasys.AdSec;
+using Oasys.Profiles;
+
+using OasysUnits;
+using OasysUnits.Units;
+
+namespace AdSecGHTests.Functions {
+  public class RebarStressStrainFunctionTests {
+    private readonly RebarStressStrainFunction _component;
+    private static SectionSolution? Solution { get; set; } = null;
+    private readonly DoubleComparer comparer = new DoubleComparer();
+
+    public RebarStressStrainFunctionTests() {
+      _component = new RebarStressStrainFunction();
+      if (Solution == null) {
+        Solution = new SolutionBuilder().Build();
+      }
+      _component.SolutionInput.Value = Solution;
+      _component.LoadInput.Value = ILoad.Create(Force.FromKilonewtons(-600), Moment.FromKilonewtonMeters(50), Moment.Zero);
+      _component.VertexInput.Value = IPoint.Create(new Length(120, LengthUnit.Millimeter), new Length(280, LengthUnit.Millimeter));
+
+    }
+
+    [Fact]
+    public void ShouldHaveErrorMessageForNullSolution() {
+      _component.SolutionInput.Value = null;
+      _component.Compute();
+      Assert.Single(_component.ErrorMessages);
+    }
+
+    [Fact]
+    public void ShouldHaveErrorMessageForNullLoad() {
+      _component.LoadInput.Value = null;
+      _component.Compute();
+      Assert.Single(_component.ErrorMessages);
+    }
+
+    [Fact]
+    public void ShouldHaveWarningForInvalidLoad() {
+      _component.LoadInput.Value = ILoad.Create(Force.Zero, Moment.Zero, Moment.Zero);
+      _component.Compute();
+      Assert.Single(_component.ErrorMessages);
+    }
+
+    [Fact]
+    public void ShouldHaveWarningForInvalidDeformation() {
+      _component.LoadInput.Value = IDeformation.Create(Strain.Zero, Curvature.Zero, Curvature.Zero);
+      _component.Compute();
+      Assert.Single(_component.ErrorMessages);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForLoad() {
+      _component.Compute();
+
+      // Check results exist
+      Assert.NotEmpty(_component.Points);
+      Assert.NotEmpty(_component.StrainsULS);
+      Assert.NotEmpty(_component.StressesULS);
+      Assert.NotEmpty(_component.StrainsSLS);
+      Assert.NotEmpty(_component.StressesSLS);
+      // Check first rebar values
+      Assert.Equal(-1011.6, _component.StrainsULS[0].As(StrainUnit.MicroStrain), comparer);
+      Assert.Equal(-326.7, _component.StrainsSLS[0].As(StrainUnit.MicroStrain), comparer);
+      Assert.Equal(-202.3, _component.StressesULS[0].As(PressureUnit.Megapascal), comparer);
+      Assert.Equal(-65.35, _component.StressesSLS[0].As(PressureUnit.Megapascal), comparer);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForDeformation() {
+      _component.LoadInput.Value = IDeformation.Create(
+        Strain.FromRatio(-0.003),
+        Curvature.Zero,
+        Curvature.Zero);
+      _component.Compute();
+
+      Assert.Equal(-3000, _component.StrainsULS[0].As(StrainUnit.MicroStrain), comparer);
+      Assert.Equal(-3000, _component.StrainsSLS[0].As(StrainUnit.MicroStrain), comparer);
+      Assert.Equal(-360.86, _component.StressesULS[0].As(PressureUnit.Megapascal), comparer);
+      Assert.Equal(-415, _component.StressesSLS[0].As(PressureUnit.Megapascal), comparer);
+    }
+
+    [Fact]
+    public void ShouldRefreshDescription() {
+      _component.StrainUnitResult = StrainUnit.MicroStrain;
+      _component.StressUnitResult = PressureUnit.Pascal;
+      _component.UpdateOutputParameter();
+
+      Assert.Contains("[µε]", _component.UlsStrainOutput.Name);
+      Assert.Contains("[µε]", _component.SlsStrainOutput.Name);
+      Assert.Contains("[Pa]", _component.UlsStressOutput.Name);
+      Assert.Contains("[Pa]", _component.SlsStressOutput.Name);
+    }
+
+    [Fact]
+    public void ShouldNotComputeIfLoadTypeIsNotCorrect() {
+      _component.LoadInput.Value = 5;
+      _component.Compute();
+      Assert.Single(_component.ErrorMessages);
+    }
+
+    [Fact]
+    public void ShouldHaveEqualNumberOfResults() {
+      _component.Compute();
+
+      var count = _component.Points.Count;
+      Assert.Equal(count, _component.StrainsULS.Count);
+      Assert.Equal(count, _component.StressesULS.Count);
+      Assert.Equal(count, _component.StrainsSLS.Count);
+      Assert.Equal(count, _component.StressesSLS.Count);
+    }
+  }
+}
diff --git a/AdSecGH/Components/2_Rebar/CreatePoint.cs b/AdSecGH/Components/2_Rebar/CreatePoint.cs
index c5b80ec4..2c49bb06 100644
--- a/AdSecGH/Components/2_Rebar/CreatePoint.cs
+++ b/AdSecGH/Components/2_Rebar/CreatePoint.cs
@@ -59,12 +59,12 @@ public class CreatePoint : DropdownAdapter<CreatePointGh> {
 
     public override void SetSelected(int i, int j) {
       _selectedItems[i] = _dropDownItems[i][j];
-      BusinessComponent.LengthUnit = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[i]);
+      BusinessComponent.LengthUnitGeometry = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[i]);
       base.UpdateUI();
     }
 
     public override void VariableParameterMaintenance() {
-      BusinessComponent.RefreshNames(BusinessComponent.LengthUnit);
+      BusinessComponent.RefreshNames(BusinessComponent.LengthUnitGeometry);
       Params.Input[0].Name = BusinessComponent.Y.Name;
       Params.Input[1].Name = BusinessComponent.Z.Name;
     }
@@ -78,13 +78,13 @@ protected override void InitialiseDropdowns() {
       _selectedItems = new List<string>();
 
       _dropDownItems.Add(UnitsHelper.GetFilteredAbbreviations(EngineeringUnits.Length));
-      _selectedItems.Add(Length.GetAbbreviation(BusinessComponent.LengthUnit));
+      _selectedItems.Add(Length.GetAbbreviation(BusinessComponent.LengthUnitGeometry));
 
       _isInitialised = true;
     }
 
     protected override void UpdateUIFromSelectedItems() {
-      BusinessComponent.LengthUnit = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[0]);
+      BusinessComponent.LengthUnitGeometry = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[0]);
       base.UpdateUIFromSelectedItems();
     }
   }
diff --git a/AdSecGH/Components/2_Rebar/CreateRebarGroup.cs b/AdSecGH/Components/2_Rebar/CreateRebarGroup.cs
index a02228f5..cd6ef8cf 100644
--- a/AdSecGH/Components/2_Rebar/CreateRebarGroup.cs
+++ b/AdSecGH/Components/2_Rebar/CreateRebarGroup.cs
@@ -29,7 +29,7 @@ public override void SetSelected(int i, int j) {
       if (i == 0) {
         BusinessComponent.SetMode((FoldMode)Enum.Parse(typeof(FoldMode), _selectedItems[i]));
       } else {
-        BusinessComponent.LengthUnit = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[i]);
+        BusinessComponent.LengthUnitGeometry = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[i]);
       }
 
       base.UpdateUI();
@@ -40,7 +40,7 @@ public override void SetSelected(int i, int j) {
     public override bool Read(GH_IReader reader) {
       var unitString = string.Empty;
       if (reader.TryGetString("LengthUnit", ref unitString)) {
-        BusinessComponent.LengthUnit = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), unitString);
+        BusinessComponent.LengthUnitGeometry = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), unitString);
         _selectedItems[1] = unitString;
       }
 
@@ -54,7 +54,7 @@ public override bool Read(GH_IReader reader) {
     }
 
     public override bool Write(GH_IWriter writer) {
-      writer.SetString("LengthUnit", BusinessComponent.LengthUnit.ToString());
+      writer.SetString("LengthUnit", BusinessComponent.LengthUnitGeometry.ToString());
       writer.SetString(modeKey, BusinessComponent.Mode.ToString());
 
       return base.Write(writer);
@@ -67,7 +67,7 @@ protected override string HtmlHelp_Source() {
 
     protected override void UpdateUIFromSelectedItems() {
       BusinessComponent.SetMode((FoldMode)Enum.Parse(typeof(FoldMode), _selectedItems[0]));
-      BusinessComponent.LengthUnit = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[1]);
+      BusinessComponent.LengthUnitGeometry = (LengthUnit)UnitsHelper.Parse(typeof(LengthUnit), _selectedItems[1]);
       base.UpdateUIFromSelectedItems();
     }
   }
diff --git a/AdSecGH/Components/6_Results/RebarStressStrain.cs b/AdSecGH/Components/6_Results/RebarStressStrain.cs
index f709d779..81776afc 100644
--- a/AdSecGH/Components/6_Results/RebarStressStrain.cs
+++ b/AdSecGH/Components/6_Results/RebarStressStrain.cs
@@ -1,155 +1,40 @@
 using System;
-using System.Collections.Generic;
 using System.Drawing;
 
-using AdSecGH.Helpers;
-using AdSecGH.Parameters;
+using AdSecCore.Functions;
+
 using AdSecGH.Properties;
 
 using AdSecGHCore.Constants;
 
 using Grasshopper.Kernel;
-using Grasshopper.Kernel.Types;
 
-using Oasys.AdSec;
-using Oasys.AdSec.Reinforcement.Groups;
-using Oasys.Profiles;
+using Oasys.GH.Helpers;
 
 using OasysGH;
-using OasysGH.Components;
-using OasysGH.Parameters;
-using OasysGH.Units;
-
-using OasysUnits;
 
 namespace AdSecGH.Components {
-  public class RebarStressStrain : GH_OasysComponent {
+  public class RebarStressStrainGh : RebarStressStrainFunction {
+    public RebarStressStrainGh() {
+    }
+  }
 
-    public RebarStressStrain() : base("Rebar Stress/Strain", "RSS",
-      "Calculate the Rebar Stress/Strains in the Section for a given Load or Deformation.", CategoryName.Name(),
-      SubCategoryName.Cat7()) {
-      Hidden = true; // sets the initial state of the component to hidden
+  public class RebarStressStrain : ComponentAdapter<RebarStressStrainGh> {
+    protected override void BeforeSolveInstance() {
+      UpdateUnit();
+      BusinessComponent.UpdateOutputParameter();
+      RefreshOutputParameter(BusinessComponent.GetAllOutputAttributes());
+    }
+
+    public RebarStressStrain() {
+      Hidden = true;
+      Category = CategoryName.Name();
+      SubCategory = SubCategoryName.Cat7();
     }
 
-    // This region handles how the component in displayed on the ribbon including name, exposure level and icon
     public override Guid ComponentGuid => new Guid("bb9fe65b-76e1-466b-be50-3dd9c7a3283f");
     public override GH_Exposure Exposure => GH_Exposure.tertiary | GH_Exposure.obscure;
     public override OasysPluginInfo PluginInfo => AdSecGH.PluginInfo.Instance;
     protected override Bitmap Icon => Resources.StressStrainRebar;
-
-    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 lengthUnitAbbreviation = Length.GetAbbreviation(DefaultUnits.LengthUnitResult);
-      string strainUnitAbbreviation = Strain.GetAbbreviation(DefaultUnits.StrainUnitResult);
-      string stressUnitAbbreviation = Pressure.GetAbbreviation(DefaultUnits.StressUnitResult);
-      pManager.AddGenericParameter($"Position [{lengthUnitAbbreviation}]", "Vx",
-        "Rebar position as 2D vertex in the section's local yz-plane ", GH_ParamAccess.list);
-      pManager.AddGenericParameter($"ULS Strain [{strainUnitAbbreviation}]", "εd",
-        "ULS strain for each rebar position", GH_ParamAccess.list);
-      pManager.AddGenericParameter($"ULS Stress [{stressUnitAbbreviation}]", "σd",
-        "ULS stress for each rebar position", GH_ParamAccess.list);
-      pManager.AddGenericParameter($"SLS Strain [{strainUnitAbbreviation}]", "εk",
-        "SLS strain for each rebar position", GH_ParamAccess.list);
-      pManager.AddGenericParameter($"SLS Stress [{stressUnitAbbreviation}]", "σk",
-        "SLS stress for each rebar position", GH_ParamAccess.list);
-    }
-
-    protected override void SolveInstance(IGH_DataAccess DA) {
-      // get solution input
-      var solution = this.GetSolutionGoo(DA, 0);
-
-      IStrengthResult uls;
-      IServiceabilityResult sls;
-
-      // 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) {
-          uls = solution.Value.Strength.Check(load.Value);
-          sls = solution.Value.Serviceability.Check(load.Value);
-        } else if (gh_typ.Value is AdSecDeformationGoo def) {
-          uls = solution.Value.Strength.Check(def.Value);
-          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;
-      }
-
-      // create flattened section to extract rebars
-      var flat = FlatSection(solution);
-
-      var pointGoos = new List<AdSecPointGoo>();
-      var outStressULS = new List<GH_UnitNumber>();
-      var outStrainULS = new List<GH_UnitNumber>();
-      var outStrainSLS = new List<GH_UnitNumber>();
-      var outStressSLS = new List<GH_UnitNumber>();
-
-      // loop through rebar groups in flattened section
-      foreach (var rebargrp in flat.ReinforcementGroups) {
-        switch (rebargrp) {
-          case ISingleBars singleBars:
-            var positions = singleBars.Positions;
-
-            if (positions == null) {
-              AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Positions are null for the rebar group.");
-              return;
-            }
-
-            foreach (var pos in positions) {
-              // position
-              pointGoos.Add(new AdSecPointGoo(pos));
-
-              // ULS strain
-              var strainULS = uls.Deformation.StrainAt(pos);
-              outStrainULS.Add(new GH_UnitNumber(strainULS.ToUnit(DefaultUnits.StrainUnitResult)));
-
-              // ULS stress in bar material from strain
-              var stressULS = singleBars.BarBundle.Material.Strength.StressAt(strainULS);
-              outStressULS.Add(new GH_UnitNumber(stressULS.ToUnit(DefaultUnits.StressUnitResult)));
-
-              // SLS strain
-              var strainSLS = sls.Deformation.StrainAt(pos);
-              outStrainSLS.Add(new GH_UnitNumber(strainSLS.ToUnit(DefaultUnits.StrainUnitResult)));
-
-              // SLS stress in bar material from strain
-              var stressSLS = singleBars.BarBundle.Material.Serviceability.StressAt(strainSLS);
-              outStressSLS.Add(new GH_UnitNumber(stressSLS.ToUnit(DefaultUnits.StressUnitResult)));
-            }
-
-            break;
-        }
-      }
-
-      DA.SetDataList(0, pointGoos);
-      DA.SetDataList(1, outStrainULS);
-      DA.SetDataList(2, outStressULS);
-      DA.SetDataList(3, outStrainSLS);
-      DA.SetDataList(4, outStressSLS);
-    }
-
-    private static ISection FlatSection(AdSecSolutionGoo solution) {
-      ISection flat;
-      if (solution.section.DesignCode != null) {
-        var adSec = IAdSec.Create(solution.section.DesignCode);
-        flat = adSec.Flatten(solution.section.Section);
-      } else {
-        var prof = IPerimeterProfile.Create(solution.section.Section.Profile);
-        flat = ISection.Create(prof, solution.section.Section.Material);
-      }
-
-      return flat;
-    }
   }
 }
diff --git a/AdSecGH/Helpers/BusinessExtensions.cs b/AdSecGH/Helpers/BusinessExtensions.cs
index d8acd901..1e1913c3 100644
--- a/AdSecGH/Helpers/BusinessExtensions.cs
+++ b/AdSecGH/Helpers/BusinessExtensions.cs
@@ -117,6 +117,12 @@ private static readonly Dictionary<Type, Func<Attribute, IGH_Param>> ToGhParam
           typeof(StrainParameter), ParamGenericObject
         }, {
           typeof(PressureParameter), ParamGenericObject
+        },{
+          typeof(StrainArrayParameter), ParamGenericObject
+        },{
+          typeof(PressureArrayParameter), ParamGenericObject
+        },{
+          typeof(PointArrayParameter), ParamGenericObject
         },
       };
 
@@ -190,7 +196,6 @@ private static readonly Dictionary<Type, Func<Attribute, object>> ToGoo
           }
         },
         { typeof(StringArrayParam), a => (a as StringArrayParam).Value },
-        // { typeof(IntegerParameter), a => (a as IntegerParameter).Value },
         {
           typeof(CrackParameter), a => {
             var crack = (a as CrackParameter).Value;
@@ -249,6 +254,33 @@ private static readonly Dictionary<Type, Func<Attribute, object>> ToGoo
             var value = (a as PressureParameter).Value;
             return new GH_UnitNumber(value.ToUnit(DefaultUnits.StressUnitResult));
           }
+        },{
+          typeof(StrainArrayParameter), a => {
+            var strains = (a as StrainArrayParameter).Value;
+            var quantityInRelevantUnit = new List<GH_UnitNumber>();
+            foreach (var strain in strains) {
+              quantityInRelevantUnit.Add(new GH_UnitNumber(strain.ToUnit(DefaultUnits.StrainUnitResult)));
+            }
+            return quantityInRelevantUnit;
+          }
+        },{
+          typeof(PressureArrayParameter), a => {
+            var strsses = (a as PressureArrayParameter).Value;
+            var quantityInRelevantUnit = new List<GH_UnitNumber>();
+            foreach (var stress in strsses) {
+              quantityInRelevantUnit.Add(new GH_UnitNumber(stress.ToUnit(DefaultUnits.StressUnitResult)));
+            }
+            return quantityInRelevantUnit;
+          }
+        },{
+          typeof(PointArrayParameter), a => {
+            var points = (a as PointArrayParameter).Value;
+             var quantityGoo = new List<AdSecPointGoo>();
+            foreach(var point in points) {
+              quantityGoo.Add(new AdSecPointGoo(point));
+            }
+            return quantityGoo;
+          }
         },
       };
 
diff --git a/AdSecGH/Helpers/ComponentAdapter.cs b/AdSecGH/Helpers/ComponentAdapter.cs
index b9f657c6..92c702d5 100644
--- a/AdSecGH/Helpers/ComponentAdapter.cs
+++ b/AdSecGH/Helpers/ComponentAdapter.cs
@@ -63,7 +63,7 @@ protected override void SolveInstance(IGH_DataAccess DA) {
     public void UpdateUnit() {
       if (BusinessComponent is Function function) {
         function.MomentUnit = DefaultUnits.MomentUnit;
-        function.LengthUnit = DefaultUnits.LengthUnitGeometry;
+        function.LengthUnitGeometry = DefaultUnits.LengthUnitGeometry;
         function.StrainUnitResult = DefaultUnits.StrainUnitResult;
         function.StressUnitResult = DefaultUnits.StressUnitResult;
         function.CurvatureUnit = DefaultUnits.CurvatureUnit;
diff --git a/AdSecGH/Helpers/DropdownAdapter.cs b/AdSecGH/Helpers/DropdownAdapter.cs
index 1657e764..4d19355a 100644
--- a/AdSecGH/Helpers/DropdownAdapter.cs
+++ b/AdSecGH/Helpers/DropdownAdapter.cs
@@ -97,7 +97,7 @@ public static Dictionary<Type, EngineeringUnits> ToEngineeringUnits() {
 
     public static Dictionary<Type, LengthUnit> ToLengthUnits(Function function) {
       return new Dictionary<Type, LengthUnit> {
-        { typeof(LengthUnit), function.LengthUnit },
+        { typeof(LengthUnit), function.LengthUnitGeometry },
       };
     }
   }
diff --git a/AdSecGHTests/Components/3_Rebar/CreateRebarGroupSaveLoadTests.cs b/AdSecGHTests/Components/3_Rebar/CreateRebarGroupSaveLoadTests.cs
index 6ea248a1..0cb244a2 100644
--- a/AdSecGHTests/Components/3_Rebar/CreateRebarGroupSaveLoadTests.cs
+++ b/AdSecGHTests/Components/3_Rebar/CreateRebarGroupSaveLoadTests.cs
@@ -51,7 +51,7 @@ public void ShouldHaveDefaultMode() {
 
     [Fact]
     public void ShouldHaveDefaultUnits() {
-      Assert.Equal("Meter", component.BusinessComponent.LengthUnit.ToString());
+      Assert.Equal("Meter", component.BusinessComponent.LengthUnitGeometry.ToString());
     }
 
     [Fact]
@@ -59,7 +59,7 @@ public void ShouldSaveAndLoadUnits() {
       component.SetSelected(1, 1);
       ComponentTesting.ComputeOutputs(component);
       var loadedComponent = SaveAndLoad();
-      Assert.Equal("Centimeter", loadedComponent.BusinessComponent.LengthUnit.ToString());
+      Assert.Equal("Centimeter", loadedComponent.BusinessComponent.LengthUnitGeometry.ToString());
     }
 
     private CreateReinforcementGroup SaveAndLoad() {
diff --git a/AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs b/AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs
new file mode 100644
index 00000000..41f683d8
--- /dev/null
+++ b/AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs
@@ -0,0 +1,68 @@
+﻿using AdSecCore.Builders;
+using AdSecCore.Functions;
+
+using AdSecGH;
+using AdSecGH.Components;
+using AdSecGH.Properties;
+
+using AdSecGHTests.Helpers;
+
+using Oasys.AdSec;
+using Oasys.GH.Helpers;
+
+using OasysUnits;
+
+using Xunit;
+
+namespace AdSecGHTests.Components {
+  [Collection("GrasshopperFixture collection")]
+  public class RebarStressStrainTests {
+    private readonly RebarStressStrain _component;
+    private static SectionSolution Solution { get; set; } = null;
+
+    public RebarStressStrainTests() {
+      _component = new RebarStressStrain();
+      if (Solution == null) {
+        Solution = new SolutionBuilder().Build();
+      }
+      ComponentTestHelper.SetInput(_component, Solution, 0);
+    }
+
+    private void SetLoad() {
+      ComponentTestHelper.SetInput(_component, ILoad.Create(
+        Force.FromKilonewtons(-600),
+        Moment.FromKilonewtonMeters(50),
+        Moment.Zero), 1);
+    }
+
+    private void SetDeformation() {
+      ComponentTestHelper.SetInput(_component, IDeformation.Create(
+        Strain.FromRatio(-0.003),
+        Curvature.Zero,
+        Curvature.Zero), 1);
+      ComponentTestHelper.ComputeData(_component);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForLoad() {
+      SetLoad();
+      ComponentTestHelper.CheckOutputIsNotNull(_component);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForDeformation() {
+      SetDeformation();
+      ComponentTestHelper.CheckOutputIsNotNull(_component);
+    }
+
+    [Fact]
+    public void ShouldHaveIconReferenced() {
+      Assert.True(_component.MatchesExpectedIcon(Resources.StressStrainRebar));
+    }
+
+    [Fact]
+    public void ShouldHavePluginInfoReferenced() {
+      Assert.Equal(PluginInfo.Instance, _component.PluginInfo);
+    }
+  }
+}
diff --git a/AdSecGHTests/Helpers/ComponentTestHelper.cs b/AdSecGHTests/Helpers/ComponentTestHelper.cs
index 87f61bbb..40eb5fbb 100644
--- a/AdSecGHTests/Helpers/ComponentTestHelper.cs
+++ b/AdSecGHTests/Helpers/ComponentTestHelper.cs
@@ -7,6 +7,8 @@
 
 using Rhino.Geometry;
 
+using Xunit;
+
 namespace AdSecGHTests.Helpers {
   public static class ComponentTestHelper {
     public static object GetOutput(GH_Component component, int index = 0, int branch = 0, int item = 0, bool forceUpdate = false) {
@@ -17,6 +19,13 @@ public static object GetOutput(GH_Component component, int index = 0, int branch
       return component.Params.Output[index].VolatileData.get_Branch(branch)[item];
     }
 
+    public static void CheckOutputIsNotNull(GH_Component component) {
+      for (int i = 0; i < component.Params.Output.Count; i++) {
+        var output = ComponentTestHelper.GetOutput(component, i);
+        Assert.NotNull(output);
+      }
+    }
+
     public static void SetInput(GH_Component component, int value, int index = 0) {
       var input = new Param_Integer();
       input.CreateAttributes();


Reviews:
user: login: psarras
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T11:46:29Z
user: login: SandeepArup
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T11:17:56Z
user: login: SandeepArup
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T14:11:29Z
user: login: SandeepArup
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T14:21:32Z
user: login: SandeepArup
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T14:25:59Z
user: login: psarras
body: minor change
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-01T09:21:25Z
user: login: SandeepArup
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:07:51Z
user: login: SandeepArup
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T10:11:14Z
user: login: psarras
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-02T11:10:35Z

Review comments:
diff_hunk: @@ -29,7 +29,15 @@ public abstract class Function : IFunction {
     public List<string> ErrorMessages { get; set; } = new List<string>();
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
-
+    public string GetStrainUnitAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
path: AdSecCore/Functions/IFunction.cs
user: login: psarras
body: nitpick: I just noticed it here. Why are we calling it `StrainUnitResult` and not `StrainUnit` like we do with `LengthUnit` are they two different things?
created_at: 2025-04-29T11:40:13Z
author_association: COLLABORATOR
diff_hunk: @@ -164,7 +164,7 @@ public override void UpdateOutputParameter() {
       MomentRangesOutput.Description = UncrackedMomentRangesOutput.Description;
     }
 
-    protected virtual bool ValidateInputs() {
+    protected virtual bool ValidateSolutionInputs() {
path: AdSecCore/Functions/ResultFunction.cs
user: login: psarras
body: maintainability: I am just seeing this. Should we not have a generic function that checks that if an input is not optional and its value is null, then we add a warning or error?
created_at: 2025-04-29T11:42:26Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,75 @@
+﻿using AdSecCore.Builders;
+using AdSecCore.Functions;
+
+using AdSecGH;
+using AdSecGH.Components;
+using AdSecGH.Properties;
+
+using AdSecGHTests.Helpers;
+
+using Oasys.AdSec;
+using Oasys.GH.Helpers;
+
+using OasysUnits;
+
+using Xunit;
+
+namespace AdSecGHTests.Components {
+  [Collection("GrasshopperFixture collection")]
+  public class RebarStressStrainTests {
+    private readonly RebarStressStrain _component;
+    private static SectionSolution Solution { get; set; } = null;
+
+    public RebarStressStrainTests() {
+      _component = new RebarStressStrain();
+      if (Solution == null) {
+        Solution = new SolutionBuilder().Build();
+      }
+      ComponentTestHelper.SetInput(_component, Solution, 0);
+    }
+
+    private void SetLoad() {
+      ComponentTestHelper.SetInput(_component, ILoad.Create(
+        Force.FromKilonewtons(-600),
+        Moment.FromKilonewtonMeters(50),
+        Moment.Zero), 1);
+    }
+
+    private void SetDeformation() {
+      ComponentTestHelper.SetInput(_component, IDeformation.Create(
+        Strain.FromRatio(-0.003),
+        Curvature.Zero,
+        Curvature.Zero), 1);
+      ComponentTestHelper.ComputeData(_component);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForLoad() {
+      SetLoad();
+      // Check all outputs exist and are not empty lists
+      for (int i = 0; i < 5; i++) {
+        var output = ComponentTestHelper.GetOutput(_component, i);
+        Assert.NotNull(output);
+      }
path: AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs
user: login: psarras
body: maintainability: export to a function that return true when all are not null. Assert on the test that this is not null. 

Better yet, make this a generic function on Component Helper?
created_at: 2025-04-29T11:45:19Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,75 @@
+﻿using AdSecCore.Builders;
+using AdSecCore.Functions;
+
+using AdSecGH;
+using AdSecGH.Components;
+using AdSecGH.Properties;
+
+using AdSecGHTests.Helpers;
+
+using Oasys.AdSec;
+using Oasys.GH.Helpers;
+
+using OasysUnits;
+
+using Xunit;
+
+namespace AdSecGHTests.Components {
+  [Collection("GrasshopperFixture collection")]
+  public class RebarStressStrainTests {
+    private readonly RebarStressStrain _component;
+    private static SectionSolution Solution { get; set; } = null;
+
+    public RebarStressStrainTests() {
+      _component = new RebarStressStrain();
+      if (Solution == null) {
+        Solution = new SolutionBuilder().Build();
+      }
+      ComponentTestHelper.SetInput(_component, Solution, 0);
+    }
+
+    private void SetLoad() {
+      ComponentTestHelper.SetInput(_component, ILoad.Create(
+        Force.FromKilonewtons(-600),
+        Moment.FromKilonewtonMeters(50),
+        Moment.Zero), 1);
+    }
+
+    private void SetDeformation() {
+      ComponentTestHelper.SetInput(_component, IDeformation.Create(
+        Strain.FromRatio(-0.003),
+        Curvature.Zero,
+        Curvature.Zero), 1);
+      ComponentTestHelper.ComputeData(_component);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForLoad() {
+      SetLoad();
+      // Check all outputs exist and are not empty lists
+      for (int i = 0; i < 5; i++) {
+        var output = ComponentTestHelper.GetOutput(_component, i);
+        Assert.NotNull(output);
+      }
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForDeformation() {
+      SetDeformation();
+      for (int i = 0; i < 5; i++) {
+        var output = ComponentTestHelper.GetOutput(_component, i);
+        Assert.NotNull(output);
+      }
path: AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs
user: login: psarras
body: reuse the function here.
created_at: 2025-04-29T11:45:28Z
author_association: COLLABORATOR
diff_hunk: @@ -29,7 +29,15 @@ public abstract class Function : IFunction {
     public List<string> ErrorMessages { get; set; } = new List<string>();
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
-
+    public string GetStrainUnitAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
path: AdSecCore/Functions/IFunction.cs
user: login: SandeepArup
body: Yes those are two different things. Also, I am aware of discrepencies and thought to resolve in another ticket but as you correctly higlight this. I have modified things 
![image](https://github.com/user-attachments/assets/eeb61d4e-5061-4274-abb4-2332b049c15a)

created_at: 2025-04-30T11:17:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,75 @@
+﻿using AdSecCore.Builders;
+using AdSecCore.Functions;
+
+using AdSecGH;
+using AdSecGH.Components;
+using AdSecGH.Properties;
+
+using AdSecGHTests.Helpers;
+
+using Oasys.AdSec;
+using Oasys.GH.Helpers;
+
+using OasysUnits;
+
+using Xunit;
+
+namespace AdSecGHTests.Components {
+  [Collection("GrasshopperFixture collection")]
+  public class RebarStressStrainTests {
+    private readonly RebarStressStrain _component;
+    private static SectionSolution Solution { get; set; } = null;
+
+    public RebarStressStrainTests() {
+      _component = new RebarStressStrain();
+      if (Solution == null) {
+        Solution = new SolutionBuilder().Build();
+      }
+      ComponentTestHelper.SetInput(_component, Solution, 0);
+    }
+
+    private void SetLoad() {
+      ComponentTestHelper.SetInput(_component, ILoad.Create(
+        Force.FromKilonewtons(-600),
+        Moment.FromKilonewtonMeters(50),
+        Moment.Zero), 1);
+    }
+
+    private void SetDeformation() {
+      ComponentTestHelper.SetInput(_component, IDeformation.Create(
+        Strain.FromRatio(-0.003),
+        Curvature.Zero,
+        Curvature.Zero), 1);
+      ComponentTestHelper.ComputeData(_component);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForLoad() {
+      SetLoad();
+      // Check all outputs exist and are not empty lists
+      for (int i = 0; i < 5; i++) {
+        var output = ComponentTestHelper.GetOutput(_component, i);
+        Assert.NotNull(output);
+      }
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForDeformation() {
+      SetDeformation();
+      for (int i = 0; i < 5; i++) {
+        var output = ComponentTestHelper.GetOutput(_component, i);
+        Assert.NotNull(output);
+      }
path: AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs
user: login: SandeepArup
body: @psarras extracted into function in component helper
created_at: 2025-04-30T14:11:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -164,7 +164,7 @@ public override void UpdateOutputParameter() {
       MomentRangesOutput.Description = UncrackedMomentRangesOutput.Description;
     }
 
-    protected virtual bool ValidateInputs() {
+    protected virtual bool ValidateSolutionInputs() {
path: AdSecCore/Functions/ResultFunction.cs
user: login: SandeepArup
body: @psarras wrote function public bool ValidateInputForNull<T>(ParameterAttribute<T> parameter)

Do let me is this what you are looking for
created_at: 2025-04-30T14:21:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,75 @@
+﻿using AdSecCore.Builders;
+using AdSecCore.Functions;
+
+using AdSecGH;
+using AdSecGH.Components;
+using AdSecGH.Properties;
+
+using AdSecGHTests.Helpers;
+
+using Oasys.AdSec;
+using Oasys.GH.Helpers;
+
+using OasysUnits;
+
+using Xunit;
+
+namespace AdSecGHTests.Components {
+  [Collection("GrasshopperFixture collection")]
+  public class RebarStressStrainTests {
+    private readonly RebarStressStrain _component;
+    private static SectionSolution Solution { get; set; } = null;
+
+    public RebarStressStrainTests() {
+      _component = new RebarStressStrain();
+      if (Solution == null) {
+        Solution = new SolutionBuilder().Build();
+      }
+      ComponentTestHelper.SetInput(_component, Solution, 0);
+    }
+
+    private void SetLoad() {
+      ComponentTestHelper.SetInput(_component, ILoad.Create(
+        Force.FromKilonewtons(-600),
+        Moment.FromKilonewtonMeters(50),
+        Moment.Zero), 1);
+    }
+
+    private void SetDeformation() {
+      ComponentTestHelper.SetInput(_component, IDeformation.Create(
+        Strain.FromRatio(-0.003),
+        Curvature.Zero,
+        Curvature.Zero), 1);
+      ComponentTestHelper.ComputeData(_component);
+    }
+
+    [Fact]
+    public void ShouldComputeCorrectlyForLoad() {
+      SetLoad();
+      // Check all outputs exist and are not empty lists
+      for (int i = 0; i < 5; i++) {
+        var output = ComponentTestHelper.GetOutput(_component, i);
+        Assert.NotNull(output);
+      }
path: AdSecGHTests/Components/6_Results/RebarStressStrainTests.cs
user: login: SandeepArup
body: @psarras extracted into function in component helper
created_at: 2025-04-30T14:25:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,6 +30,36 @@ public abstract class Function : IFunction {
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
 
+    public string GetStrainUnitResultAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
+    }
+    public string GetStressUnitResultAbbreviation() {
+      return Pressure.GetAbbreviation(StressUnitResult);
+    }
+    public string GetLengthUnitGeometryAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitGeometry);
+    }
+    public string GetLengthUnitResultAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitResult);
+    }
+
+    public virtual bool ValidateInputs() {
+      var inputs = GetAllInputAttributes();
+      foreach (var input in inputs) {
+        if (input == null) {
path: AdSecCore/Functions/IFunction.cs
user: login: psarras
body: shouldn't we only check the non-optional inputs?
created_at: 2025-05-01T09:17:26Z
author_association: COLLABORATOR
diff_hunk: @@ -30,6 +30,36 @@ public abstract class Function : IFunction {
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
 
+    public string GetStrainUnitResultAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
+    }
+    public string GetStressUnitResultAbbreviation() {
+      return Pressure.GetAbbreviation(StressUnitResult);
+    }
+    public string GetLengthUnitGeometryAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitGeometry);
+    }
+    public string GetLengthUnitResultAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitResult);
+    }
+
+    public virtual bool ValidateInputs() {
+      var inputs = GetAllInputAttributes();
+      foreach (var input in inputs) {
+        if (input == null) {
path: AdSecCore/Functions/IFunction.cs
user: login: psarras
body: although I have a feeling, that those will be triggered by GH, as well. I am curious if you can hit this line from a test while testing in AdSecGH.Tests

created_at: 2025-05-01T09:18:05Z
author_association: COLLABORATOR
diff_hunk: @@ -30,6 +30,36 @@ public abstract class Function : IFunction {
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
 
+    public string GetStrainUnitResultAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
+    }
+    public string GetStressUnitResultAbbreviation() {
+      return Pressure.GetAbbreviation(StressUnitResult);
+    }
+    public string GetLengthUnitGeometryAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitGeometry);
+    }
+    public string GetLengthUnitResultAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitResult);
+    }
+
+    public virtual bool ValidateInputs() {
+      var inputs = GetAllInputAttributes();
+      foreach (var input in inputs) {
+        if (input == null) {
path: AdSecCore/Functions/IFunction.cs
user: login: SandeepArup
body: This can not be triggered by GH side as we create parameter first on component before we proceed further.
created_at: 2025-05-01T11:07:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,6 +30,36 @@ public abstract class Function : IFunction {
     public List<string> WarningMessages { get; set; } = new List<string>();
     public List<string> RemarkMessages { get; set; } = new List<string>();
 
+    public string GetStrainUnitResultAbbreviation() {
+      return Strain.GetAbbreviation(StrainUnitResult);
+    }
+    public string GetStressUnitResultAbbreviation() {
+      return Pressure.GetAbbreviation(StressUnitResult);
+    }
+    public string GetLengthUnitGeometryAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitGeometry);
+    }
+    public string GetLengthUnitResultAbbreviation() {
+      return Length.GetAbbreviation(LengthUnitResult);
+    }
+
+    public virtual bool ValidateInputs() {
+      var inputs = GetAllInputAttributes();
+      foreach (var input in inputs) {
+        if (input == null) {
path: AdSecCore/Functions/IFunction.cs
user: login: SandeepArup
body: @psarras Have remove null check of parameter
created_at: 2025-05-02T10:11:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-02T10:04:22Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=arup-group_AdSec-Grasshopper&pullRequest=164) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=arup-group_AdSec-Grasshopper&pullRequest=164&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=arup-group_AdSec-Grasshopper&pullRequest=164&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=arup-group_AdSec-Grasshopper&pullRequest=164&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=arup-group_AdSec-Grasshopper&pullRequest=164&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=arup-group_AdSec-Grasshopper&pullRequest=164&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=arup-group_AdSec-Grasshopper&pullRequest=164)


author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-02T11:11:28Z
body: ## [Codecov](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) Report
Attention: Patch coverage is `98.91304%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 68.6%. Comparing base [(`1ff3fe7`)](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/commit/1ff3fe76cdea05c1b47c53cc811e368dd7fe6cf5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) to head [(`519f539`)](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/commit/519f5392615346bfbca47fb5fa7d1c6cbc436dc2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) | Patch % | Lines |
|---|---|---|
| [AdSecCore/Functions/IFunction.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FIFunction.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9JRnVuY3Rpb24uY3M=) | 92.3% | [1 Missing :warning: ](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) |
| [AdSecGH/Components/2\_Rebar/CreatePoint.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecGH%2FComponents%2F2_Rebar%2FCreatePoint.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNHSC9Db21wb25lbnRzLzJfUmViYXIvQ3JlYXRlUG9pbnQuY3M=) | 75.0% | [1 Missing :warning: ](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) |

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164/graphs/tree.svg?width=650&height=150&src=pr&token=9D8OF3ELL2&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group)](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group)

```diff
@@           Coverage Diff           @@
##            main    #164     +/-   ##
=======================================
+ Coverage   67.6%   68.6%   +0.9%     
=======================================
  Files        119     120      +1     
  Lines       7349    7427     +78     
  Branches     823     822      -1     
=======================================
+ Hits        4969    5095    +126     
+ Misses      2271    2223     -48     
  Partials     109     109             
```

| [Files with missing lines](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) | Coverage Δ | |
|---|---|---|
| [...dSecCore/Functions/ConcreteStressStrainFunction.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FConcreteStressStrainFunction.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9Db25jcmV0ZVN0cmVzc1N0cmFpbkZ1bmN0aW9uLmNz) | `98.8% <100.0%> (+<0.1%)` | :arrow_up: |
| [AdSecCore/Functions/ParametersGeneric.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FParametersGeneric.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9QYXJhbWV0ZXJzR2VuZXJpYy5jcw==) | `100.0% <ø> (ø)` | |
| [AdSecCore/Functions/PointRebarFunction.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FPointRebarFunction.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9Qb2ludFJlYmFyRnVuY3Rpb24uY3M=) | `100.0% <100.0%> (ø)` | |
| [AdSecCore/Functions/RebarGroupFunction.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FRebarGroupFunction.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9SZWJhckdyb3VwRnVuY3Rpb24uY3M=) | `99.3% <100.0%> (ø)` | |
| [AdSecCore/Functions/RebarStressStrainFunction.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FRebarStressStrainFunction.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9SZWJhclN0cmVzc1N0cmFpbkZ1bmN0aW9uLmNz) | `100.0% <100.0%> (ø)` | |
| [AdSecCore/Functions/ResultFunction.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecCore%2FFunctions%2FResultFunction.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNDb3JlL0Z1bmN0aW9ucy9SZXN1bHRGdW5jdGlvbi5jcw==) | `100.0% <100.0%> (ø)` | |
| [AdSecGH/Components/2\_Rebar/CreateRebarGroup.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecGH%2FComponents%2F2_Rebar%2FCreateRebarGroup.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNHSC9Db21wb25lbnRzLzJfUmViYXIvQ3JlYXRlUmViYXJHcm91cC5jcw==) | `92.8% <100.0%> (ø)` | |
| [AdSecGH/Components/6\_Results/RebarStressStrain.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecGH%2FComponents%2F6_Results%2FRebarStressStrain.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNHSC9Db21wb25lbnRzLzZfUmVzdWx0cy9SZWJhclN0cmVzc1N0cmFpbi5jcw==) | `100.0% <100.0%> (+65.3%)` | :arrow_up: |
| [AdSecGH/Helpers/BusinessExtensions.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecGH%2FHelpers%2FBusinessExtensions.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNHSC9IZWxwZXJzL0J1c2luZXNzRXh0ZW5zaW9ucy5jcw==) | `97.4% <100.0%> (+0.2%)` | :arrow_up: |
| [AdSecGH/Helpers/ComponentAdapter.cs](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree&filepath=AdSecGH%2FHelpers%2FComponentAdapter.cs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group#diff-QWRTZWNHSC9IZWxwZXJzL0NvbXBvbmVudEFkYXB0ZXIuY3M=) | `100.0% <100.0%> (ø)` | |
| ... and [3 more](https://app.codecov.io/gh/arup-group/AdSec-Grasshopper/pull/164?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=arup-group) | |

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE