dlubal-software / RFEM_Python_Client

Python client (or high-level functions) for RFEM 6 using Web Services, SOAP and WSDL
https://dlubal-software.github.io/.github/
MIT License
71 stars 27 forks source link

QUESTION: Clarification on Excessive Results for Member Internal Forces Retrieval #343

Open Jeroen124 opened 9 months ago

Jeroen124 commented 9 months ago

I am currently working on optimizing data retrieval from RFEM models using the Python API, particularly focusing on member internal forces. However, I've encountered an issue where the number of results retrieved seems excessively high for a simple model scenario. Specifically, for a single beam, I am receiving 16 entries for a specific member query and 31 entries when querying all members. This seems counterintuitive, especially considering the model's simplicity.

Here's the code I am using for a simple cantilever:

from RFEM.LoadCasesAndCombinations.loadCasesAndCombinations import LoadCasesAndCombinations
from RFEM.LoadCasesAndCombinations.loadCase import LoadCase
from RFEM.Results.resultTables import ResultTables
from RFEM.BasicObjects.material import Material
from RFEM.BasicObjects.member import Member
from RFEM.BasicObjects.node import Node
from RFEM.BasicObjects.section import Section
from RFEM.TypesForNodes.nodalSupport import NodalSupport
from RFEM.Loads.nodalLoad import NodalLoad
from RFEM.LoadCasesAndCombinations.loadCombination import LoadCombination

from RFEM.LoadCasesAndCombinations.staticAnalysisSettings import StaticAnalysisSettings
from RFEM.enums import (
    AnalysisType,
    CaseObjectType,
    ImperfectionDirection,
    ImperfectionType,
    MemberImperfectionDefinitionType,
    MemberImperfectionType,
    SetType,
    NodalSupportType,
    NodalLoadDirection,
    ActionCategoryType
)

Model(new_model=True, model_name="Test", delete_all=True)
Model.clientModel.service.begin_modification("new")

# Making the model as simple as possible
LoadCasesAndCombinations(
    {
        "activate_combination_wizard_and_classification": False,
        "activate_combination_wizard": False,
        "result_combinations_active": False,
        "result_combinations_parentheses_active": False,
        "result_combinations_consider_sub_results": False,
        "combination_name_according_to_action_category": False,
    }
)

Material(1, 'S235')
Section(1, 'IPE 200')

Node(1, 0.0, 0.0, 0.0)
Node(2, 1, 0.0, 0.0)

Member(1, 1, 2, 0.0, 1, 1)

NodalSupport(1, '1', NodalSupportType.FIXED)

StaticAnalysisSettings.GeometricallyLinear(1, "Linear")

LoadCase.StaticAnalysis(1, 'Self-Weight',analysis_settings_no=1,self_weight=[True, 0.0, 0.0, 1.0])
NodalLoad(1, 1, '1', NodalLoadDirection.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W, 1*1000)

LoadCombination(
    no=1,
    analysis_type=AnalysisType.ANALYSIS_TYPE_STATIC,
    name='Loadcomb1',
    consider_imperfection=False,
    combination_items=[[1, 1, 0, False]],
)

# Calculate
Calculate_all()

Model.clientModel.service.finish_modification()

# =============================================================================
# Results
# =============================================================================
AllMemberResults =ResultTables.MembersInternalForces(
    loading_type=CaseObjectType.E_OBJECT_TYPE_LOAD_COMBINATION, loading_no=1, object_no=0
)

SpecificMemberResults =ResultTables.MembersInternalForces(
    loading_type=CaseObjectType.E_OBJECT_TYPE_LOAD_COMBINATION, loading_no=1, object_no=1
)

print(len(AllMemberResults))
print(len(SpecificMemberResults))

Given the model consists of a single beam, I expected the results to be more streamlined – perhaps detailed results at the nodes and an overall summary for the member itself, rather than 16 or 31 distinct entries.

Could you please provide insight into:

Why the number of results entries is so high for a single member? Is there a way to interpret these results more effectively, or a method to streamline the data retrieval to focus on key outputs? Are there best practices or documentation available that guide the handling of results data, especially for simple models? Any guidance or references to documentation would be greatly appreciated as I navigate optimizing my workflow with the RFEM Python API.

Jeroen124 commented 9 months ago

This problem seems closely related to discussion 271: https://github.com/Dlubal-Software/RFEM_Python_Client/discussions/271

OndraMichal commented 8 months ago

Hi @Jeroen124, it is no so counter intuitive if you understand that you are retrieving following tables: object_no = 0 ... all tables incl. total max/min, 31 lines object_no = 1 ... table for member 1, 16 lines image

If you want you can use directly

Model.clientModel.service.get_results_for_members_internal_forces(loading_type.name, loading_no, object_no)

skipping ConvertResultsToListOfDct() function.

Jeroen124 commented 8 months ago

@OndraMichal Thank you for the feedback!

Your feedback has clarified the results we're obtaining. We're curious if there's a way to streamline the process by retrieving only the first two rows. (not the extremes) . Since we're handling the data in Python, and we are only retrieving 1 result at a time, we can do the filtering and finding max & min later in Python, potentially speeding up the overall process.

Additionally, maybe we can perhaps reduce the complexity of the ConvertResultsToListOfDct() function by minimizing the use of nested try-except blocks? This will increase the efficiency as well?

Would submitting a PR for fixing the ConvertResultsToListOfDct() help?

Please let me know your thoughts. 👍

gvandewiel86 commented 8 months ago

I was running into a similar problem and was also wondering how to distinguish between different members when requesting MEMBERS_INTERNAL_FORCES with object_no=0. As the output is not providing the member number, only the node numbers. Hence you end up with a whole bunch of data without the member reference, given that a lot of "additional" data is provided.

When retrieving MEMBERS_INTERNAL_FORCES_BY_SECTION the member_number is provided.

heetrojivadiya commented 8 months ago

I was running into a similar problem and was also wondering how to distinguish between different members when requesting MEMBERS_INTERNAL_FORCES with object_no=0. As the output is not providing the member number, only the node numbers. Hence you end up with a whole bunch of data without the member reference, given that a lot of "additional" data is provided.

When retrieving MEMBERS_INTERNAL_FORCES_BY_SECTION the member_number is provided.

Hello @gvandewiel86,

thank you for reaching out to us. I have tested ResultTables.MembersInternalForces(object_no=0) and you are correct. It doesn't include member numbers in the result while the default value for include_base is set as False so which means only attributes belonging to row are extracted for result tables and converted to a list of dictionaries.

(members_internal_forces_row){
         no = 14
         description = "1"
         row =
            (members_internal_forces){
               node_number =
                  (variant){
                     type = 2
                     value = "1"
                  }
               location = 0.0
               location_flags = "M"
               internal_force_label = "M<sub>y</sub>"
               internal_force_n = -4355.875
               internal_force_vy = 0.0
               internal_force_vz = -185.8513946533203
               internal_force_mt = 0.0
               internal_force_my = 617.5512084960938
               internal_force_mz = 0.0
               specification = "Beam | 1 - IPE 200 | L : 10.000 m"
            }

But if you use ResultTables.MembersInternalForces(object_no=0, include_base = True) then you can extract the complete result table including no and description, where no represents the row number and description represents the content of the member number column.

For example:

result = ResultTables.MembersInternalForces(object_no=0, include_base=True)
print(result[13])  # row number 

output: {'no': 14.0, 'description': 1.0, 'internal_force_vz': -185.8513946533203, 'internal_force_my': 617.5512084960938, 'internal_force_mz': 0.0, 'internal_force_n': -4355.875, 'internal_force_label': 'M<sub>y</sub>', 'internal_force_mt': 0.0, 'location': 0.0, 'node_number': 1.0, 'internal_force_vy': 0.0, 'specification': 'Beam | 1 - IPE 200 | L : 10.000 m', 'location_flags': 'M'}

So, in the above part of the result 'no' = 14.0 is row number 14, and 'description' = 1.0 is member number 1, which can be seen in the below image.

image

I hope this answer clarifies your issue. If not then feel free to contact us.

heetrojivadiya commented 7 months ago

@OndraMichal Thank you for the feedback!

Your feedback has clarified the results we're obtaining. We're curious if there's a way to streamline the process by retrieving only the first two rows. (not the extremes) . Since we're handling the data in Python, and we are only retrieving 1 result at a time, we can do the filtering and finding max & min later in Python, potentially speeding up the overall process.

Additionally, maybe we can perhaps reduce the complexity of the ConvertResultsToListOfDct() function by minimizing the use of nested try-except blocks? This will increase the efficiency as well?

Would submitting a PR for fixing the ConvertResultsToListOfDct() help?

Please let me know your thoughts. 👍

Hello @Jeroen124,

I understood your concern that you want to omit extremes rows in result tables and will find max/min from first two rows for each members later. But in some cases max/min values are not on member start and end node (see figure). So, you may need to get extremes rows too. Although, you can omit repeating rows of extremes by adding few lines in your code to optimize result table.

results = ResultTables.MembersInternalForces(object_no=0, include_base=True)

result, combos, combo = [], [], ()

for item in results:
    description = item.get('description')
    location = item.get('location')
    node = item.get('node_number')
    if isinstance(description, float) or description == 'Extremes':
        if description == 'Extremes':
            description = results[results.index(item) - 1].get('description')
        if location != None: combo = (description, node, location)
        if combo not in combos:
            combos.append(combo)
            item.pop('no', None)
            result.append(item)

print(result)

image

By implementing above lines in script, we can get only important lines from result tables. As soon as possible we will update this in some important static methods of ResultTables by additional parameters something like without_extremes = True/False.

image

Jeroen124 commented 7 months ago

Hi @heetrojivadiya thank you for your answer. As previously mentioned we are currently trying to speed up the results retrieval process from RFEM. With this goal in mind, we are actively scanning the market for the fastest API solvers to overcome this challenge, hopefully RFEM can improve this process. Although filtering after retrieving the results is an option, our priority is to streamline the entire result collection process.

heetrojivadiya commented 7 months ago

Hi @heetrojivadiya thank you for your answer. As previously mentioned we are currently trying to speed up the results retrieval process from RFEM. With this goal in mind, we are actively scanning the market for the fastest API solvers to overcome this challenge, hopefully RFEM can improve this process. Although filtering after retrieving the results is an option, our priority is to streamline the entire result collection process.

Hey @Jeroen124, I understood your issue of time-consuming. You may follow discussion #339 for speeding up the data extraction process.