Skip to main content

Easily extract results from Pynite... with pynite_reporting!

PyniteFEA is excellent and it is generally design-ready...if it weren't for all the trouble we have to go through to get results out. This is not unique to Pynite, most FEA programs require a significant amount of post-processing to prepare the actual analysis results for design.

Enter pynite_reporting

This package provides a series of functions that consume a Pynite.FEModel3D object and returns consistently-structured dictionaries of analysis results.

Note: As of 2025-06-30, this package has only been "casually tested" (meaning simple visual checking of outputs). No test suite has been written (but is coming).

Installation

pip install pynite_reporting

Dependencies

  • Numpy (>= 2.0.0)

(PyNiteFEA is not a dependency but it is assumed to be in your working environment)

Pynite Compatibility

PyniteFEA >= 1.0.0

(Not compatible with pre-v1.0 versions!)

Examples (typical use)

from Pynite import FEModel3D
import pynite_reporting as pr

model = FEModel3D(...) # Build your model here

# Selected load combinations in your model
lcs = [
    # 'LC1', 
    'LC2',
    'LC3',
    # 'LC4', 
    # 'LC5',
]

# All the below functions optionally take a list of load combos
# so you can select which combos to extract

## Additionally, each function accepts a results_key parameter.
## This optional parameter is set to a default str value, unique for each function.
## When you set the results_key=None, then your results tree will be one level shallower.

# Return reactions for all supports, all load combos
reactions = pr.extract_node_reactions(
    model,
    # load_combinations=lcs,
    # results_key=None
)

# Returns all node deflections for all load combos
node_deflections = pr.extract_node_deflections(
    model,
    # load_combinations=lcs,
    # results_key=None
)

# Return force arrays for all members, all load combos
force_arrays = pr.extract_member_arrays(
    model,
    # n_points=1000,
    # as_lists=False,
    # load_combinations=lcs,
    # results_key=None
)

# Return force min/max/absmax envelope for all members, all load combos
# Values will not necessarily be at concurrent locations
forces_minmax = pr.extract_member_envelopes(
    model,
    # load_combinations=lcs,
    # results_key=None
)

# Return force min/max envelope for each span in all members, all load combos
forces_minmax_spans = pr.extract_span_envelopes(
    model,
    # load_combinations=lcs,
    # results_key=None
)

# Return forces for all load combos at specific locations along the global member length
forces_at_locations = pr.extract_member_actions_by_location(
    model, 
    force_extraction_locations={"Member01": [0, 2000, 3600]},
    # load_combinations=lcs,
    # results_key=None
)

# Return forces for all load combos at 1/4 points for *each span* of the given members
forces_at_location_ratios = pr.extract_member_actions_by_location(
    model, 
    force_extraction_ratios={"Member05": [0.25, 0.5, 0.75]}, 
    by_span=True,
    # load_combinations=lcs,
    # results_key=None
    )

And there you have it! Does that not make your life a little bit easier?

Merge result trees

NEW in v0.2.0

If you are planning on serializing your results to a JSON file, then you can merge all of your result tree (dictionaries) into a single dictionary by using merge_trees:

e.g.

merged_tree = pr.merge_trees([force_arrays, forces_minmax, forces_at_locations])

Convenience functions

There are one-line function for writing/reading dictionaries to JSON files:

pr.to_json("results.json", merged_tree)

round_tripped_tree = pr.from_json("results.json")

FYI (Opinions at work!)

I have made the decision to remove unnecessary results from being returned by some of these functions.

"???WHAA??? I want to see ALL of my results!!!", you say?

I don't think you actually do. Consider the following small amount of results:

{
    'M_col': {
        'shear': {
            'Fy': {
                'LC1': {'max': 0, 'min': 0}, # No loading for this load case on this member
                'LC2': {'max': 4000, 'min': 4000}
            },
            'Fz': {
                'LC1': {'max': 10000, 'min': 10000}, 
                'LC2': {'max': 0, 'min': 0} # No loading for this either...
            },
            
        },
        'moment': {
            'Mz': {
                'LC1': {'max': 0, 'min': 0}, # Or this...
                'LC2': {'max': 20000, 'min': 0}
            }, 
            'My': {
                'LC1': {'max': 50000, 'min': 50000},
                'LC2': {'max': 0, 'min': 0} # Or this...
            }
        },
        ...
    }
}

The above results contain unnecessary data. This structure has loading in the gravity direction and the transverse direction, each on a different load case/combo.

The load cases that show as 0, 0 indicate that the force diagrams are completely flat and without activity.

To avoid confusion in reading and to prevent unnecessary iterations (if you are putting these results through an automated process), I have filtered out the keys that result in null values.

Here is how the above results are returned:

    'M_col': {
        'shear': {
            'Fy': {
                'LC2': {'max': 4000, 'min': 4000}
            },
            'Fz': {
                'LC1': {'max': 10000, 'min': 10000}, 
            },
        },
        'moment': {
            'Mz': {
                'LC2': {'max': 20000, 'min': 0}
            }, 
            'My': {
                'LC1': {'max': 50000, 'min': 50000}
            }
        },
        ...
    },

So, you know that all other load combos result in null values without having to physically read a bunch of zeros or confusing "near zero" values.

The tolerance for this is an absolute tolerance of 1e-7. Currently, this is not parameterized and is hard-coded into the package (because it was easier and made the function signatures cleaner). So, even if you have REALLY small result values (on the order of 0.000001 units), those values will still be returned to you (and not excluded).

This is allows you to see all concurrent forces for a load combination at a given location.

Metadata

Release files for pynite-reporting 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pynite-reporting 0.3.0
File Size Uploaded Provenance
pynite_reporting-0.3.0.tar.gz 49.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pynite-reporting 0.3.0
File Interpreter ABI Platform
pynite_reporting-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 63.0 kB

Release files / pynite_reporting-0.3.0.tar.gz

Download URL pynite_reporting-0.3.0.tar.gz
Size 49.7 kB
Tags Source
SHA-256 checksum
How to use checksums
dab955e43d97d0bdf161a8e5a71d754059c48946886d4e8b18bf7b93adbdbdb3
BLAKE2b-256 checksum
How to use checksums
a2cd87aafbaed128c127aa1b62173a1a617e49f0a8b7f3476c3da10020d3c51c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.23

Release files / pynite_reporting-0.3.0-py3-none-any.whl

Download URL pynite_reporting-0.3.0-py3-none-any.whl
Size 13.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02b00714169e77682432c7dd036ba4f79274e75743f3240b690680a137496d2c
BLAKE2b-256 checksum
How to use checksums
0ea988c842137674917913c241a847cc462fec3c7628109feab3df20aa974ff5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.23

Security for pynite-reporting 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page