FCS Analysis & Design Check Pipeline

Extracted from the fcs-HBC-2-tacr production codebase (AnalysisSocket/StructureCheck.fcs, FemAnalysis.fcs).


1. Architecture Overview

The full pipeline follows this flow:

LoadGroups   ─┐
LoadCases    ─┤
Combinations ─┤──► AnalysisCase ──► FEM Solver ──► BeamsFemResults ──► StructureCheck
Model        ─┘                                          │
                                                         ├──► Internal Forces (extremes)
                                                         ├──► Displacements (extremes)
                                                         ├──► Reactions (resultants)
                                                         ├──► Eigenvalues (stability)
                                                         └──► Design Checks ──► Report

2. AnalysisCase — Central Orchestrator

AnalysisCase = res.comp.analysisCase{
   AnalysisClass = ModelClass,                     # the model with geometry & loads

   MeshConnect = res.comp.analysisCase.MeshConnect{
      ConnectRules = Fcs.Mesh.ConnectRules{
         Rules = [Fcs.Mesh.ConnectRules.ConnectClub{ Entities = Fcs.Assembly.All }]
      },
      AutoConnect           = True,
      OneMeshElementPerBeam = False,               # False = refined mesh
      WeldNodes             = True,
      ElementSize           = 0.4,                 # target element size [m]
      DefaultElementType2D  = Fcs.Mesh.Element.Quadrilateral,
   },

   ResultCase    = Fcs.Analysis.ResultCase(Fcs.Action.ResultClass{ Name = "All ULS-Fundamental" }),
   ResultCaseULS = ResultCase,
   ResultCaseSLS = Fcs.Analysis.ResultCase(Fcs.Action.ResultClass{ Name = "All SLS characteristic" }),

   LoadCases        = ModelClass.loadCases,          # list of FitLoadCase objects
   LoadGroups       = combinations.loadGroups.All,   # top-level FitLoadGroup tree
   LoadCombinations = combinations.allEnvelopes,     # list of FitLoadEnvelope objects
   ResultClasses    = combinations.resultClasses.All, # list of FitResultClass objects

   globalStabilityLinCombination = [],
   globalStabilityLoadCaseName   = [],
}

Key Properties

Property Type Purpose
AnalysisClass gclass Model containing geometry, beams, loads
MeshConnect object Mesh generation settings
ResultCase ResultCase Which combo set to use for extremes
LoadCases list All FitLoadCase objects
LoadGroups list Top-level FitLoadGroup hierarchy
LoadCombinations list Generated envelopes / combos
ResultClasses list Named groups of combos

3. MeshConnect — Mesh Settings

MeshConnect = res.comp.analysisCase.MeshConnect{
   ConnectRules = Fcs.Mesh.ConnectRules{
      Rules = [
         Fcs.Mesh.ConnectRules.ConnectClub{ Entities = Fcs.Assembly.All }
      ]
   },
   AutoConnect           = True,    # auto-connect overlapping nodes
   OneMeshElementPerBeam = False,   # True = 1 element/beam (coarse), False = refined
   WeldNodes             = True,    # merge coincident nodes
   ElementSize           = 0.4,     # target element size [m]
   DefaultElementType2D  = Fcs.Mesh.Element.Quadrilateral,
}

ConnectRules options:


4. ResultCase — Extracting Results for Specific Combo Sets

# For ULS results (named group must match one of the ResultClasses):
ResultCaseULS = Fcs.Analysis.ResultCase(Fcs.Action.ResultClass{ Name = "All ULS-Fundamental" })

# For SLS results:
ResultCaseSLS = Fcs.Analysis.ResultCase(Fcs.Action.ResultClass{ Name = "All SLS characteristic" })

The Name string must match one of the names in ResultClasses, which are auto-generated by the combination engine. The ResultCase tells the solver which envelope to evaluate when extracting min/max extremes.


5. BeamsFemResults — Per-Beam Result Extraction

NumberOfBeams   = BeamParts.Count

BeamsFemResults = res.f.EnumerableRange(NumberOfBeams).Select(beamId =>
   res.comp.SimpleBeamAnalysisResults{
      AnalysisCase,
      units,
      SingleBeam    = BeamParts[beamId],
      BeamsCount    = RepetitiveBeamsCount,    # for repetitive member counts
      BeamPathFn    = beamPathFn(beamId),
      IgnoredLayers = [],
      SupportVertexes = ModelClass.supportVertex,
      beamId,
   }
)

Results Available on Each Beam

Property Content
beam.ExtOnBeam.dx.Max/Min Displacement extremes
beam.ExtremeForcesOnResultParts Internal force extremes
beam.ExtremeDisplacementsOnResultParts Displacement extremes per part
beam.ExtremeResultants Support reactions
beam.lambdaEigenModesLoadCase Stability eigenvalues per load case
beam.lambdaEigenModesLinCombination Stability eigenvalues for combinations
beam.SingleBeam Beam geometry / section info

6. StructureCheck — Design Check Pipeline

Setup

analysisSocketReportSetup = Fcs.Reporting.Setup{
   LatexSyntax = True,
   Units       = Fcs.Units.Setup(Fcs.Units.DefaultSteel, ...),
   Collapsible = True,
   Numbered    = True,
}

Check Class Creation

SimpleBeamCheckByCodeClass := res.en.steel.EnSimpleBeamCheck

AllBeamsCheckClasses = res.f.EnumerableRange(NumberOfBeams).Select(beam =>
   SimpleBeamCheckByCodeClass{
      AnalysisCase     = Analysis.AnalysisCase,
      SingleBeam       = Analysis.BeamParts[beam],
      BeamsCount       = Analysis.RepetitiveBeamsCount,
      BeamPathFn       = Analysis.beamPathFn(beam),
      IgnoredLayers    = Analysis.IgnoredLayers,
      AllBeamsSelector = Fcs.Assembly.BeamByPath(memberPathStringFn(beam)),
      GlobalDisplacementLengths,
      GlobalDisplacementConditions,
      units,
   }
)

Available Check Flags

Flag Check Type
isAxialMomentCheckOn beam.AllAxialMomentChecks
isAxialForceCheckOn beam.AllAxialForceChecks
isBendingMomentAndShearCheckOn beam.AllBendingMomentChecks
isShearCheckOn beam.AllShearForceChecks
isBucklingYyCheckOn beam.AllBucklingYyChecks
isBucklingZzCheckOn beam.AllBucklingZzChecks
isAxialAndBendingBucklingCheckOn beam.AllAxialAndBendingBucklingChecks
isAxialAndBendingBucklingYyCheckOn beam.AllAxialAndBendingBucklingYyChecks
isAxialAndBendingBucklingZzCheckOn beam.AllAxialAndBendingBucklingZzChecks
isDisplacementCheckOn beam.AllDisplacementChecks
isAnyGlobalDisplacementCheckOn beam.AllGlobalDisplacementChecks

Check Result Structure

Each check returns a collection of singleCheck objects:

Aggregate Results

# Max utilization across all beams
maxCheckByBones = AllBeamsCheckBones.Select(beam =>
   beam.Select(checks =>
      checks.Select(singleCheck =>
         (singleCheck.Count > 0) ? (singleCheck.Max()) : (0)
      )
   )
)

# Pass/fail per beam
AreChecksOk = res.f.EnumerableRange(NumberOfBeams).Select(beam =>
   AllBeamsCheckClasses[beam].partCheckBoolFn(ChecksOnAllBeams[beam], True).All(ch => ch == True)
)

7. Singularity Detection

isModelSingularCalc = (
   AllBeamsFemResults.Select(b => [
      Abs([b.ExtOnBeam.dx.Max.Value, b.ExtOnBeam.dy.Max.Value, b.ExtOnBeam.dz.Max.Value].Max()),
      Abs([b.ExtOnBeam.dx.Min.Value, b.ExtOnBeam.dy.Min.Value, b.ExtOnBeam.dz.Min.Value].Min()),
   ].Max()).Max() >= 10 * Unit.m
)

If any displacement exceeds 10 metres the model is considered singular (unstable).


8. Geometry Table for Report

geometryHeaderRow = [
   Fcs.Reporting.Text("Id"),
   Fcs.Reporting.Text("Name"),
   Fcs.Reporting.Text("Layer"),
   Fcs.Reporting.Text("Length"),
   Fcs.Reporting.Text("Section"),
   Fcs.Reporting.Text("Start coordinates"),
   Fcs.Reporting.Text("End coordinates"),
]

# Per-beam row with LCS origin/end extraction:
lcsToOriginCoorsFn = fnLcs => ("(" +
   fnLcs.Origin.X.ToString("0.000") + ", " +
   fnLcs.Origin.Y.ToString("0.000") + ", " +
   fnLcs.Origin.Z.ToString("0.000") + ")")

9. Quick Reference

# 1. Wrap model + combos in an AnalysisCase
ac = res.comp.analysisCase{
   AnalysisClass    = MyModel,
   MeshConnect      = ...,
   LoadCases        = MyModel.loadCases,
   LoadGroups       = combos.loadGroups.All,
   LoadCombinations = combos.allEnvelopes,
   ResultClasses    = combos.resultClasses.All,
   ResultCase       = Fcs.Analysis.ResultCase(Fcs.Action.ResultClass{ Name = "All ULS-Fundamental" }),
}

# 2. Extract per-beam results
beamResults = res.f.EnumerableRange(nBeams).Select(id =>
   res.comp.SimpleBeamAnalysisResults{ AnalysisCase = ac, SingleBeam = beams[id], beamId = id })

# 3. Run design checks
checks = res.f.EnumerableRange(nBeams).Select(id =>
   res.en.steel.EnSimpleBeamCheck{ AnalysisCase = ac, SingleBeam = beams[id] })

# 4. Check if model is singular
isSingular = beamResults.Select(b => b.ExtOnBeam.dx.Max.Value).Max() >= 10