Skip to content

Commit ae82626

Browse files
feat(etabs): adds more extractors (#1026)
* feat: adds `BaseReact` extractor * refactor: repeating strings under constants * fix: array processing only * feat: adds `PierForce`extractor * feat: adds `SpandrelForce` extractor * feat: adds `StoryDrifts` extractor * fix: missing key in selection shouldn't throw * feat: adds `JointReact` extractor
1 parent 880d777 commit ae82626

File tree

12 files changed

+499
-17
lines changed

12 files changed

+499
-17
lines changed

Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/Helpers/CsiResultsExtractorFactory.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
3+
using Speckle.Converters.CSiShared.Utils;
34

45
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
56

@@ -15,7 +16,12 @@ public CsiResultsExtractorFactory(IServiceProvider serviceProvider)
1516
public IApplicationResultsExtractor GetExtractor(string resultsKey) =>
1617
resultsKey switch
1718
{
18-
"FrameForces" => _serviceProvider.GetRequiredService<CsiFrameForceResultsExtractor>(),
19+
ResultsKey.BASE_REACT => _serviceProvider.GetRequiredService<CsiBaseReactResultsExtractor>(),
20+
ResultsKey.FRAME_FORCES => _serviceProvider.GetRequiredService<CsiFrameForceResultsExtractor>(),
21+
ResultsKey.JOINT_REACT => _serviceProvider.GetRequiredService<CsiJointReactResultsExtractor>(),
22+
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
23+
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
24+
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
1925
_ => throw new InvalidOperationException($"{resultsKey} not accounted for in CsiResultsExtractorFactory")
2026
};
2127
}

Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ CancellationToken cancellationToken
146146
foreach (var resultType in requestedResultTypes)
147147
{
148148
var extractor = _resultsExtractorFactory.GetExtractor(resultType);
149-
var objectNames = objectSelectionSummary[extractor.TargetObjectType];
149+
objectSelectionSummary.TryGetValue(extractor.TargetObjectType, out var objectNames);
150150
analysisResults[extractor.ResultsKey] = extractor.GetResults(objectNames);
151151
}
152152
rootObjectCollection["analysisResults"] = analysisResults;

Connectors/CSi/Speckle.Connectors.CSiShared/Settings/ResultTypeSetting.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Speckle.Connectors.DUI.Settings;
2+
using Speckle.Converters.CSiShared.Utils;
23

34
namespace Speckle.Connectors.CSiShared.Settings;
45

@@ -8,5 +9,5 @@ public class ResultTypeSetting(List<string> values) : ICardSetting
89
public string? Title { get; set; } = "Result Type";
910
public string? Type { get; set; } = "array";
1011
public object? Value { get; set; } = values;
11-
public List<string>? Enum { get; set; } = ["FrameForces"];
12+
public List<string>? Enum { get; set; } = ResultsKey.All.OrderBy(x => x).ToList();
1213
}

Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ public static IServiceCollection AddCsiConverters(this IServiceCollection servic
1818

1919
// Register property extractors
2020
serviceCollection.AddScoped<CsiFramePropertiesExtractor>();
21-
serviceCollection.AddScoped<CsiFrameForceResultsExtractor>();
2221
serviceCollection.AddScoped<CsiJointPropertiesExtractor>();
2322
serviceCollection.AddScoped<CsiShellPropertiesExtractor>();
2423
serviceCollection.AddScoped<DatabaseTableExtractor>();
2524
serviceCollection.AddScoped<DisplayValueExtractor>();
26-
serviceCollection.AddScoped<ResultsArrayProcessor>();
2725
serviceCollection.AddScoped<SharedPropertiesExtractor>();
2826

27+
// Register results extractors
28+
serviceCollection.AddScoped<CsiBaseReactResultsExtractor>();
29+
serviceCollection.AddScoped<CsiFrameForceResultsExtractor>();
30+
serviceCollection.AddScoped<CsiJointReactResultsExtractor>();
31+
serviceCollection.AddScoped<CsiPierForceResultsExtractor>();
32+
serviceCollection.AddScoped<CsiSpandrelForceResultsExtractor>();
33+
serviceCollection.AddScoped<CsiStoryDriftsResultsExtractor>();
34+
serviceCollection.AddScoped<ResultsArrayProcessor>();
35+
2936
// Register connector caches
3037
serviceCollection.AddScoped<CsiToSpeckleCacheSingleton>();
3138

Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@
1818
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SpeckleApplicationIdExtensions.cs" />
1919
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs" />
2020
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
21+
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiBaseReactResultsExtractor.cs" />
2122
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiFrameForceResultsExtractor.cs" />
2223
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiFramePropertiesExtractor.cs" />
2324
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiJointPropertiesExtractor.cs" />
25+
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiJointReactResultsExtractor.cs" />
26+
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiPierForceResultsExtractor.cs" />
2427
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiShellPropertiesExtractor.cs" />
28+
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiSpandrelForceResultsExtractor.cs" />
29+
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiStoryDriftsResultsExtractor.cs" />
2530
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiToSpeckleCacheSingleton.cs" />
2631
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\DatabaseTableExtractor.cs" />
2732
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\IApplicationResultsExtractor.cs" />
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using Speckle.Converters.Common;
2+
using Speckle.Converters.CSiShared.Utils;
3+
4+
namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers;
5+
6+
public class CsiBaseReactResultsExtractor : IApplicationResultsExtractor
7+
{
8+
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
9+
private readonly ResultsArrayProcessor _resultsArrayProcessor;
10+
11+
public string ResultsKey => "baseReact";
12+
public ModelObjectType TargetObjectType => ModelObjectType.JOINT;
13+
public ResultsConfiguration Configuration { get; } =
14+
new(["LoadCase", "Wrap:StepNum"], ["FX", "FY", "FZ", "MX", "ParamMy", "MZ"]);
15+
16+
public CsiBaseReactResultsExtractor(
17+
IConverterSettingsStore<CsiConversionSettings> settingsStore,
18+
ResultsArrayProcessor resultsArrayProcessor
19+
)
20+
{
21+
_settingsStore = settingsStore;
22+
_resultsArrayProcessor = resultsArrayProcessor;
23+
}
24+
25+
// NOTE: since these are global reactions, they're independent of the user selection, therefore discarded
26+
public Dictionary<string, object> GetResults(IEnumerable<string>? _)
27+
{
28+
// Step 1: define api variables
29+
int numberResults = 0;
30+
string[] loadCase = [],
31+
stepType = [];
32+
double[] stepNum = [],
33+
fx = [],
34+
fy = [],
35+
fz = [],
36+
mx = [],
37+
paramMy = [],
38+
mz = [];
39+
double gx = 0,
40+
gy = 0,
41+
gz = 0;
42+
43+
// Step 2: api call
44+
int success = _settingsStore.Current.SapModel.Results.BaseReact(
45+
ref numberResults,
46+
ref loadCase,
47+
ref stepType,
48+
ref stepNum,
49+
ref fx,
50+
ref fy,
51+
ref fz,
52+
ref mx,
53+
ref paramMy,
54+
ref mz,
55+
ref gx,
56+
ref gy,
57+
ref gz
58+
);
59+
60+
if (success != 0 || numberResults == 0)
61+
{
62+
throw new InvalidOperationException("Base reaction extraction failed."); // shouldn't fail silently
63+
}
64+
65+
// Step 3: organise arrays for dictionary processor
66+
var rawArrays = new Dictionary<string, object>
67+
{
68+
["LoadCase"] = loadCase,
69+
["StepNum"] = stepNum,
70+
["FX"] = fx,
71+
["FY"] = fy,
72+
["FZ"] = fz,
73+
["MX"] = mx,
74+
["ParamMy"] = paramMy,
75+
["MZ"] = mz
76+
};
77+
78+
// Step 4: return sorted and processed dictionary
79+
var resultsDictionary = _resultsArrayProcessor.ProcessArrays(rawArrays, Configuration);
80+
81+
// Step 5: add the extra centroid information
82+
resultsDictionary["GX"] = gx;
83+
resultsDictionary["GY"] = gy;
84+
resultsDictionary["GZ"] = gz;
85+
86+
// Step 6: return
87+
return resultsDictionary;
88+
}
89+
}

Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFrameForceResultsExtractor.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public sealed class CsiFrameForceResultsExtractor : IApplicationResultsExtractor
88
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
99
private readonly ResultsArrayProcessor _resultsArrayProcessor;
1010

11-
public string ResultsKey => "FrameForces";
11+
public string ResultsKey => "frameForces";
1212
public ModelObjectType TargetObjectType => ModelObjectType.FRAME;
1313

1414
public ResultsConfiguration Configuration { get; } =
@@ -29,22 +29,22 @@ public Dictionary<string, object> GetResults(IEnumerable<string>? objectNames =
2929
var frameNames = objectNames?.ToList();
3030
if (frameNames is null || frameNames.Count == 0)
3131
{
32-
throw new InvalidOperationException("Frame names are required for force extraction");
32+
throw new InvalidOperationException("Frame(s) are required in the selection for results extraction");
3333
}
3434

3535
// Step 2: single dictionary to accumulate all results
3636
var allArrays = new Dictionary<string, List<object>>
3737
{
38-
["Elm"] = new(),
39-
["ElmSta"] = new(),
40-
["LoadCase"] = new(),
41-
["StepNum"] = new(),
42-
["P"] = new(),
43-
["V2"] = new(),
44-
["V3"] = new(),
45-
["T"] = new(),
46-
["M2"] = new(),
47-
["M3"] = new()
38+
["Elm"] = [],
39+
["ElmSta"] = [],
40+
["LoadCase"] = [],
41+
["StepNum"] = [],
42+
["P"] = [],
43+
["V2"] = [],
44+
["V3"] = [],
45+
["T"] = [],
46+
["M2"] = [],
47+
["M3"] = []
4848
};
4949

5050
// Step 3: define api variables
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using Speckle.Converters.Common;
2+
using Speckle.Converters.CSiShared.Utils;
3+
4+
namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers;
5+
6+
public class CsiJointReactResultsExtractor : IApplicationResultsExtractor
7+
{
8+
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
9+
private readonly ResultsArrayProcessor _resultsArrayProcessor;
10+
11+
public string ResultsKey => "jointReact";
12+
public ModelObjectType TargetObjectType => ModelObjectType.JOINT;
13+
public ResultsConfiguration Configuration { get; } =
14+
new(["Elm", "LoadCase", "Wrap:StepNum"], ["F1", "F2", "F3", "M1", "M2", "M3"]);
15+
16+
public CsiJointReactResultsExtractor(
17+
IConverterSettingsStore<CsiConversionSettings> settingsStore,
18+
ResultsArrayProcessor resultsArrayProcessor
19+
)
20+
{
21+
_settingsStore = settingsStore;
22+
_resultsArrayProcessor = resultsArrayProcessor;
23+
}
24+
25+
public Dictionary<string, object> GetResults(IEnumerable<string>? objectNames = null)
26+
{
27+
// Step 1: validate input
28+
var jointNames = objectNames?.ToList();
29+
if (jointNames is null || jointNames.Count == 0)
30+
{
31+
throw new InvalidOperationException("Joint(s) are required in the selection for results extraction");
32+
}
33+
34+
// Step 2: single dictionary to accumulate all results
35+
var allArrays = new Dictionary<string, List<object>>
36+
{
37+
["Elm"] = [],
38+
["LoadCase"] = [],
39+
["StepNum"] = [],
40+
["F1"] = [],
41+
["F2"] = [],
42+
["F3"] = [],
43+
["M1"] = [],
44+
["M2"] = [],
45+
["M3"] = []
46+
};
47+
48+
// Step 3: define api variables
49+
int numberResults = 0;
50+
string[] obj = [],
51+
elm = [],
52+
loadCase = [],
53+
stepType = [];
54+
double[] stepNum = [],
55+
f1 = [],
56+
f2 = [],
57+
f3 = [],
58+
m1 = [],
59+
m2 = [],
60+
m3 = [];
61+
62+
// Step 4: iterate through objectNames and get joint reaction results for those that are assigned restraints / springs and grounded
63+
foreach (string jointName in jointNames)
64+
{
65+
// this only works if the joint has restraints or springs assignments, so check if it's a valid query first
66+
bool[] restraints = [];
67+
string springAssignment = string.Empty;
68+
_settingsStore.Current.SapModel.PointObj.GetRestraint(jointName, ref restraints);
69+
_settingsStore.Current.SapModel.PointObj.GetSpringAssignment(jointName, ref springAssignment);
70+
if (restraints.All(r => !r) && string.IsNullOrEmpty(springAssignment))
71+
{
72+
continue; // skip this joint - it has neither restraints nor springs
73+
}
74+
75+
int success = _settingsStore.Current.SapModel.Results.JointReact(
76+
jointName,
77+
eItemTypeElm.ObjectElm,
78+
ref numberResults,
79+
ref obj,
80+
ref elm,
81+
ref loadCase,
82+
ref stepType,
83+
ref stepNum,
84+
ref f1,
85+
ref f2,
86+
ref f3,
87+
ref m1,
88+
ref m2,
89+
ref m3
90+
);
91+
92+
if (success != 0)
93+
{
94+
throw new InvalidOperationException($"Joint force extraction failed for frame {jointName}."); // shouldn't fail silently
95+
}
96+
97+
// accumulate results
98+
allArrays["Elm"].AddRange(elm.Cast<object>());
99+
allArrays["LoadCase"].AddRange(loadCase.Cast<object>());
100+
allArrays["StepNum"].AddRange(stepNum.Cast<object>());
101+
allArrays["F1"].AddRange(f1.Cast<object>());
102+
allArrays["F2"].AddRange(f2.Cast<object>());
103+
allArrays["F3"].AddRange(f3.Cast<object>());
104+
allArrays["M1"].AddRange(m1.Cast<object>());
105+
allArrays["M2"].AddRange(m2.Cast<object>());
106+
allArrays["M3"].AddRange(m3.Cast<object>());
107+
}
108+
109+
// Step 5: organise arrays for dictionary processor
110+
var rawArrays = allArrays.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value.ToArray());
111+
112+
// Step 6: return sorted and processed dictionary
113+
return _resultsArrayProcessor.ProcessArrays(rawArrays, Configuration);
114+
}
115+
}

0 commit comments

Comments
 (0)