/*** Public methods ****************************************************************************************************/ /// <summary> /// Public method that calculates beam results using beam input values, supports, and loads, entered by User. /// Method saves results to public string CommonItems.stringBeamResultsthat can be retrieved elsewhere. /// </summary> public static void CalculateBeamResults() { //Debug.WriteLine("CalculateResults.CalculateBeamResults(): Method entered."); // Initialize output string with beam properties. Output results will be added to this string as they are calculated. CommonItems.stringOutputResults = CreateBeamPropertiesString(); // Create a new instance of Scheme class with static scheme information. IScheme iScheme = new Scheme(); // Create a new instance of Material class. IMaterial iMaterial = iScheme.IMaterialNew(); iMaterial.IntNumber = 1; // Potentially can use multiple materials across beam length. iMaterial.StringName = CommonItems.stringNameMaterial; iMaterial.DoubleYoungsModulus = CommonItems.doubleYoungsModulus; iMaterial.DoublePoissonsRatio = CommonItems.doublePoissonsRatio; iScheme.ListIMaterial.Add(iMaterial); // Add an instance of Material class to the list of materials // Create a new instance of CrossSection class. ICrossSection iCrossSection = iScheme.ICrossSectionNew(); iCrossSection.IntNumber = 1; // Potentially can use multiple cross sections across beam length. iCrossSection.StringName = CommonItems.stringNameCrossSection; iCrossSection.DoubleAreaMomentOfInertia = CommonItems.doubleInertia; iScheme.ListICrossSection.Add(iCrossSection); // Add an instance of CrossSection class to the list of cross sections // Create sorted list of INodes that contains all supports and all concentrated loads. List <INode> listINodesSorted = BuildNodeList(); // Following is dubug code that shows listINodesSorted by position to verify if nodes sorted properly. //int intNodeIndex = 0; //foreach (INode iNodeItem in listINodesSorted) //{ // Debug.WriteLine($"iNodeItem[{intNodeIndex}]: DoubleNodePosition={iNodeItem.DoubleNodePosition}, BoolNodeSupport={iNodeItem.BoolNodeSupport}"); // intNodeIndex++; //} // // Create multiple new instances of INode from listINodesSorted. int intINodeCounter = 1; // First node number alway is 1. INode iNode; foreach (INode iNodeItem in listINodesSorted) { iNode = iScheme.INodeNew(); iNode.DoubleNodePosition = iNodeItem.DoubleNodePosition; iNode.IntNumber = intINodeCounter++; iNode.BoolNodeSupport = iNodeItem.BoolNodeSupport; iNode.IntNodeDofDisplacement = iNodeItem.IntNodeDofDisplacement; iNode.IntNodeDofRotation = iNodeItem.IntNodeDofRotation; iNode.DoubleNodeForce = iNodeItem.DoubleNodeForce; iNode.DoubleNodeMoment = iNodeItem.DoubleNodeMoment; iScheme.ListINode.Add(iNode); } listINodesSorted.Clear(); // Clean up some by clearing original list values since not needed after copy to iScheme.ListINode. // Send node output values to Debug! //Debug.WriteLine($"\nCalculateResults.cs.CalculateResults(): List iScheme.ListINode should be sorted by DoubleNodePosition. scheme.ListINode.Count={iScheme.ListINode.Count}"); //foreach (INode iNodeItem in iScheme.ListINode) //{ // Debug.WriteLine($" DoubleNodePosition={iNodeItem.DoubleNodePosition}, IntNumber={iNodeItem.IntNumber}, BoolNodeSupport={iNodeItem.BoolNodeSupport}, IntNodeDofDisplacement={iNodeItem.IntNodeDofDisplacement}, IntNodeDofRotation={iNodeItem.IntNodeDofRotation}, DoubleNodeForce={iNodeItem.DoubleNodeForce}, DoubleNodeMoment={iNodeItem.DoubleNodeMoment}"); //} //Debug.WriteLine(""); // // Create multiple new instances of IElement. Create a new element between each INode in iScheme.ListINode. for (int i = 1; i < iScheme.ListINode.Count; i++) { IElement iElement = iScheme.IElementNew(); iElement.IntNumber = i; iElement.IntNodeLeft = i; iElement.IntNodeRight = i + 1; iElement.IntMaterial = 1; // Note: Underlying FEM methods allow use of different material per element. iElement.IntCrossSection = 1; // Note: Underlying FEM methods allow use of different CrossSection per element. iScheme.ListIElement.Add(iElement); } //Debug.WriteLine($"\n\nCalculateResults.cs.CalculateResults(): iScheme.ListINode.Count={iScheme.ListINode.Count}, scheme.ListIElement.Count={iScheme.ListIElement.Count}"); //foreach (IElement iElement in iScheme.ListIElement) //{ // Debug.WriteLine($" iElement Values: IntNumber={iElement.IntNumber}, IntNodeLeft={iElement.IntNodeLeft}, IntNodeRight={iElement.IntNodeRight}"); //} // // Create multiple new instances of ILoad. int intLoadCounter = 1; foreach (INode iNodeItem in iScheme.ListINode) { if (!iNodeItem.BoolNodeSupport) // Skip following if not a load node. { ILoad iLoad = iScheme.ILoadNew(); iLoad.IntNumber = intLoadCounter++; iLoad.IntNode = iNodeItem.IntNumber; // Assigned load to corresponding INode. iLoad.DoubleForce = iNodeItem.DoubleNodeForce; // Downward forces are negative. iLoad.DoubleMoment = iNodeItem.DoubleNodeMoment; // Clockwise moments are negative. iScheme.ListILoad.Add(iLoad); } } //Debug.WriteLine($"\n\nCalculateResults.cs.CalculateResults(): scheme.ListILoad.Count={iScheme.ListILoad.Count}"); //foreach (ILoad iLoad in iScheme.ListILoad) //{ // Debug.WriteLine($" iLoad Values: IntNumber={iLoad.IntNumber}, IntNode={iLoad.IntNode}, DoubleForce={iLoad.DoubleForce}, DoubleMoment={iLoad.DoubleMoment}"); // Debug.WriteLine($" iLoad Values: IntNumber={iLoad.IntNumber}, StringName={iLoad.StringName}, IntNode={iLoad.IntNode}, DoubleForce={iLoad.DoubleForce}, DoubleMoment={iLoad.DoubleMoment}"); //} // // Create new instance of IModel. IModel iModel = new Model(iScheme); iModel.AssemblyModel(); if (CommonItems.boolMatricesShow) { // Display calculated matrix data for iMmodel. CommonItems.stringOutputResults += DisplayMatrices.DisplayResultsIModel(iModel); } // Create new instance of ISolver. ISolver iSolver = new Solver(iModel); iSolver.AssembleFiniteElementModel(); iSolver.Run(); if (CommonItems.boolMatricesShow) { // Display calculated matrix data for iSolver. CommonItems.stringOutputResults += DisplayMatrices.DisplayResultsISolver(iSolver); } // Create a new instance of PostProcessor to initialize values. Discard result since not used. _ = new PostProcessor(iScheme, iModel, iSolver); PostProcessor.CalculateNodalResults(); if (CommonItems.boolMatricesShow) { // Display calculated data for iPostProcessor. CommonItems.stringOutputResults += DisplayMatrices.DisplayResultsPostProcessor(); } // // Next 3 foreach loops output shear, moment, and deflection results. // int intIntervals = Convert.ToInt32(CommonItems.doubleBeamLength / CommonItems.doubleOutputSegmentLength); //Debug.WriteLine($"\nCalculateResults(): intIntervals={intIntervals}"); // Save absolute maximum shear, moment, and deflection values found for output below. Values always have positive sign since absolute values. double doubleMaxValueShear = 0d; double doubleMaxValueMoment = 0d; double doubleMaxValueDeflection = 0d; Dictionary <double, double> dictionary; // CommonItems.stringOutputResults += "\n\n****** Shear results ******\n"; dictionary = PostProcessor.GetDictionaryShear(intIntervals); // Output shear plot results and save maximum shear value found. foreach (KeyValuePair <double, double> keyValuePair in dictionary) { if (Math.Abs(keyValuePair.Value) > doubleMaxValueShear) { doubleMaxValueShear = Math.Abs(keyValuePair.Value); } CommonItems.stringOutputResults += $"\n{keyValuePair.Key,CommonItems.intPadOutput:0.0000} {keyValuePair.Value,CommonItems.intPadOutput:0.0}"; } // CommonItems.stringOutputResults += "\n\n****** Moment results ******\n"; dictionary = PostProcessor.GetDictionaryMoment(intIntervals); // Output moment plot results and save maximum moment value found. foreach (KeyValuePair <double, double> keyValuePair in dictionary) { if (Math.Abs(keyValuePair.Value) > doubleMaxValueMoment) { doubleMaxValueMoment = Math.Abs(keyValuePair.Value); } CommonItems.stringOutputResults += $"\n{keyValuePair.Key,CommonItems.intPadOutput:0.0000} {keyValuePair.Value,CommonItems.intPadOutput:0.0}"; } // CommonItems.stringOutputResults += "\n\n****** Deflection results ******\n"; dictionary = PostProcessor.GetDictionaryDeflection(intIntervals); // Output deflection plot results and save maximum deflection value found. foreach (KeyValuePair <double, double> keyValuePair in dictionary) { if (Math.Abs(keyValuePair.Value) > doubleMaxValueDeflection) { doubleMaxValueDeflection = Math.Abs(keyValuePair.Value); } // Note: 'keyValuePair.Value,CommonItems.intPadOutput:0.00000' in next line should have same number of trailing zeros as value of PostProcessor.intRoundDigits CommonItems.stringOutputResults += $"\n{keyValuePair.Key,CommonItems.intPadOutput:0.0000} {keyValuePair.Value,CommonItems.intPadOutput:0.00000}"; } // dictionary.Clear(); // Done with dictionary so clear content. CommonItems.stringOutputResults += $"\n\n****** Beam results: ******\n"; CommonItems.stringOutputResults += $"\n{CommonItems.stringConstLoadForce} {CommonItems.stringConstLoadMoment}"; double doubleSupportPosition; double doubleSupportForce; double doubleSupportMoment; int intIndex = 0; foreach (INode iNodeScheme in iScheme.ListINode) { //Debug.WriteLine($"intIndex={intIndex}"); if (iNodeScheme.BoolNodeSupport) { // Node is a support so retrieve an display support position, force, and moment. doubleSupportPosition = iNodeScheme.DoubleNodePosition; doubleSupportForce = iSolver.DoubleMatrixReactions[intIndex, 0]; doubleSupportMoment = iSolver.DoubleMatrixReactions[++intIndex, 0]; CommonItems.stringOutputResults += $"\n\nSupport location: {doubleSupportPosition,0:0.0000} {CommonItems.stringConstUnitsLength}"; // Round following values before display to eliminate roundoff error that can occur at a support when value should really be zero. CommonItems.stringOutputResults += $"\n Reaction force: {Math.Round(doubleSupportForce, CommonItems.intRoundDigits, MidpointRounding.AwayFromZero).ToString(LibNum.fpNumericFormatSeparator)} {CommonItems.stringConstUnitsForce}"; CommonItems.stringOutputResults += $"\n Reaction moment: {Math.Round(doubleSupportMoment, CommonItems.intRoundDigits, MidpointRounding.AwayFromZero).ToString(LibNum.fpNumericFormatSeparator)} {CommonItems.stringConstUnitsMoment}"; } else { // This node is not a support so skip it. intIndex++; } intIndex++; } CommonItems.stringOutputResults += $"\n\nAbsolute maximum values of shear, moment, and deflection found."; // Round following values before display to eliminate roundoff error. CommonItems.stringOutputResults += $"\n Shear: {Math.Round(doubleMaxValueShear, CommonItems.intRoundDigits, MidpointRounding.AwayFromZero).ToString(LibNum.fpNumericFormatSeparator)} {CommonItems.stringConstUnitsForce}"; CommonItems.stringOutputResults += $"\n Moment: {Math.Round(doubleMaxValueMoment, CommonItems.intRoundDigits, MidpointRounding.AwayFromZero).ToString(LibNum.fpNumericFormatSeparator)} {CommonItems.stringConstUnitsMoment}"; CommonItems.stringOutputResults += $"\n Deflection: {Math.Round(doubleMaxValueDeflection, CommonItems.intRoundDigits, MidpointRounding.AwayFromZero).ToString(LibNum.fpNumericFormatNone)} {CommonItems.stringConstUnitsLength}"; // // // NOTE: This is a beam analysis application versus a beam design application. // Following link shows many deflection limits that could be checked in a beam design application. Save link for future reference. // AISC Steel Construction Manual, 14th Edition, Deflection Limits: http://www.bgstructuralengineering.com/BGSCM14/BGSCM008/Deflection/BGSCM0080402.htm // // Comment out following Debug lines after confirming specific FEM case matches AISC Steel Construction formula results. // To test, uncomment one sample case below and then enter matching beam and load configuration. Can only test one case at a time! // Conclusion: Application returns results that closely match AISC Steel Construction formula results. // //if (CommonItems.listLoadConcentratedValues.Count == 1) // DebugSamples.CheckResultsSampleBeam01(CommonItems.listLoadConcentratedValues[0]); // //if (CommonItems.listLoadConcentratedValues.Count == 2) // DebugSamples.CheckResultsSampleBeam02(CommonItems.listLoadConcentratedValues[0], CommonItems.listLoadConcentratedValues[1]); // //if (CommonItems.listLoadConcentratedValues.Count == 1) // DebugSamples.CheckResultsSampleBeam03(CommonItems.listLoadConcentratedValues[0]); // //if (CommonItems.listLoadConcentratedValues.Count == 1) // DebugSamples.CheckResultsSampleBeam04(CommonItems.listLoadConcentratedValues[0]); // //if (CommonItems.listLoadUniformValues.Count == 1) // DebugSamples.CheckResultsSampleBeam05(CommonItems.listLoadUniformValues[0]); // //if (CommonItems.listLoadUniformValues.Count == 1) // DebugSamples.CheckResultsSampleBeam06(CommonItems.listLoadUniformValues[0]); // //if (CommonItems.listLoadUniformValues.Count == 2) // DebugSamples.CheckResultsSampleBeam07(CommonItems.listLoadUniformValues[0]); // //DebugSamples.CheckResultsDoubleFormatting(); }