Skip to main content

FEMAP neutral file parser

Project description

FEMAP neutral file parser

  • Free software: MIT license

Features and limitations

Parse and render FEMAP neutral files. For now, three blocks are interpreted:

  • Block 100 “Neutral File Header”

  • Block 450 “Output Sets”

  • Block 451 “Output Data Vectors”

Usage

To use FEMAP neutral Parser in a project:

>>> from femap_neutral_parser import Parser

To instantiate a new parser, just pass a file path:

>>> neu = Parser("tests/data/mystran_00.NEU")

To have a list of available blocks:

>>> neu.available_blocks()
{'header': 100, 'output_sets': 450, '_output_vectors': 451}

Or maybe in a more human-friendly way is to use info() method, which prints available results and outputs:

>>> neu.info()
<BLANKLINE>
Analysis
========
 * subcase 1: Analyse. NASTRAN SPC 1 - lc1. test (MYSTRAN::Static)
 * subcase 2: Analyse. NASTRAN SPC 1 - lc2. test (MYSTRAN::Static)
<BLANKLINE>
Outputs
=======
access to one of them using `._output_vectors[<title>][<SubcaseID>]['record']
<BLANKLINE>
 * displacements::t_total -> RSS translation
 * displacements::t1 -> T1 translation
 * displacements::t2 -> T2 translation
 * ...
 * cbar_stress::min_comb_a -> BAR EndA Min Stress
 * cbar_stress::min_comb_b -> BAR EndB Min Stress

Access available blocs by attribute:

>>> from pprint import pprint as pp
>>> pp(neu.output_sets)
{1: {'anal_type': 'Static',
     'from_prog': 'Unknown',
     'integer_format': None,
     'notes': '',
     'process_type': None,
     'title': 'Analyse. NASTRAN SPC 1 - lc1. test',
     'value': 0.0},
 2: {'anal_type': 'Static',
     'from_prog': 'Unknown',
     'integer_format': None,
     'notes': '',
     'process_type': None,
     'title': 'Analyse. NASTRAN SPC 1 - lc2. test',
     'value': 0.0}}

Low-level data access

Under the hood, output vectors (block451) are organized as nested dictionaries [<vector title>][<LCID>]:

>>> pp(neu._output_vectors["displacements::t_total"][2])
{'abs_max': 2.578386,
 'calc_warn': True,
 'cent_total': True,
 'comp_dir': 1,
 'component_vec': [10002.0,
                   10003.0,
                   10004.0,
                   0.0,
                   0.0,
                   0.0,
                   0.0,
                   0.0,
                   0.0,
                   0.0],
 'id_max': 7,
 'id_min': 1,
 'max_val': 2.578386,
 'min_val': 0.0,
 'record': array([( 1, 0.000000e+00), ( 2, 2.045391e-01), ( 3, 0.000000e+00),
       ( 4, 1.468270e-02), ( 5, 9.231050e-05), ( 6, 6.276400e-01),
       ( 7, 2.578386e+00), ( 8, 1.025100e-01), ( 9, 2.578363e+00),
       (10, 1.916094e+00), (11, 1.100510e+00), (12, 2.389742e+00)],
      dtype=[('NodeID', '<i8'), ('displacements::t_total', '<f8')]),
 'vecID': 10001}

Aggregated Outputs

Slightly highest level than _output_vectors access, aggregated output is available using Parser.vectors() method. For example, to get all outputs for translations vectors:

>>> arr = neu.vectors(("displacements::t1", "displacements::t2", "displacements::t3"))
>>> arr
rec.array([( 1,  0.        , 0.,  0.000000e+00, 1),
           ( 2, -0.1870816 , 0.,  0.000000e+00, 1),
           ...
           (11,  0.        , 0., -1.100510e+00, 2),
           (12,  0.        , 0., -2.389742e+00, 2)],
          dtype=[('NodeID', '<i8'), ('displacements::t1', '<f8'), ('displacements::t2', '<f8'), ('displacements::t3', '<f8'), ('SubcaseID', '<i8')])

Returned value is a numpy structured array (https://numpy.org/doc/stable/user/basics.rec.html). If Pandas is available, you can request to have a DataFrame instead:

>>> neu.vectors(("displacements::t1", "displacements::t2", "displacements::t3"), asdf=True)
                   displacements::t1  displacements::t2  displacements::t3
SubcaseID NodeID
1         1       0.000000       0.0  0.000000
          2      -0.187082       0.0  0.000000
...
          11      0.000000       0.0 -0.956073
          12      0.000000       0.0 -1.602912
2         1       0.000000       0.0  0.000000
          2      -0.204539       0.0  0.000000
...
          11      0.000000       0.0 -1.100510
          12      0.000000       0.0 -2.389742

You can also request sub-cases IDs, or request raw headers:

>>> neu.vectors(("displacements::t1", "displacements::t2", "displacements::t3"), asdf=True, raw=True,
...              SubcaseIDs=2)
                  T1 translation  T2 translation  T3 translation
SubcaseID NodeID
2         1             0.000000             0.0        0.000000
          2            -0.204539             0.0        0.000000
...
          11            0.000000             0.0       -1.100510
          12            0.000000             0.0       -2.389742

High-Level access

At highest level, you can use the get method that already organize vectors for you:

>>> neu.get(what="displacements", asdf=True)
                        t1   t2        t3        r1        r2            r3
SubcaseID NodeID
1         1       0.000000  0.0  0.000000 -0.000432  0.008923  4.699029e-03
          2      -0.187082  0.0  0.000000  0.000432  0.008923  4.666047e-03
...
2         1       0.000000  0.0  0.000000 -0.000007  0.009755  5.137517e-03
          2      -0.204539  0.0  0.000000  0.000007  0.009755  5.101457e-03
...
          11      0.000000  0.0 -1.100510  0.000000  0.010988  1.006013e-06
          12      0.000000  0.0 -2.389742  0.000000  0.004149  1.360364e-08

One can get the list of high-level shortcuts using Parser.get_vectors() which will return a set of available headers:

>>> neu.get_vectors() == {
... 'elem_gpf',
... 'force_vectors',
... 'displacements',
... 'summed_gpf',
... 'mpc_forces',
... 'constraint_gpf',
... 'cbar_force',
... 'cbar_stress',
... 'cbush_force',
... 'cbush_stress',
... 'cbar_ms',
... 'applied_gpf',
... 'spc_forces',
... 'cbeam_force'
... }
True

Requirements

Beside Python>=3.8, only numpy is required. numpy arrays are released as https://numpy.org/doc/stable/user/basics.rec.html, which makes conversions to Pandas a breeze.

If Pandas is installed (which is advised), the asdf parameters are valid.

Testing

For testing, making docs or coding, all the dev requirements are provided in requirements_dev.txt.

From a blank virtual environment, clone this repo:

git clone https://framagit.org/numenic/femap_neutral_parser.git

Create a Python virtual environment, and activate it:

python -m venv fnp
source fnp/bin/activate

Install requirements:

cd femap_neutral_parser
pip install -r requirements.txt  # install numpy
pip install -r requirements_dev.txt
pip install -e .  # install femap-neutral-parser in new venv

Now testing:

make test  # or make coverage

Building docs:

make docs

History

0.8.3 (2021-09-12)

Major internal refactoring

  • output_vectors becomes internal (_output_vectors). The recommended ways to access data is now Parser.vectors() or Parser.get() at a higher level.

  • no more autotranslate initialisation parameter. Everything is harmonized under the hood.

0.6.0 (2021-07-12)

  • add engineering shortcuts

  • harmonize NodeID and SubcaseID with pynastran

  • vectors are now case insensible

0.5.0 (2021-04-16)

  • improve parsing speed on large files

0.4.0 (2021-04-16)

add:

  • Parser.info()

  • Parser.vectors()

0.3.0 (2021-04-16)

  • add Parser.info() and Parser.vectors()

  • update documentation

  • add info() doprint option

0.2.0 - 0.3.0 (2021-04-16)

  • Update Documentation

0.1.0 (2021-03-24)

First release on PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

femap_neutral_parser-0.14.0.tar.gz (46.4 kB view hashes)

Uploaded Source

Built Distribution

femap_neutral_parser-0.14.0-py2.py3-none-any.whl (23.6 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page