diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 969be2d2..9a984835 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,10 @@ name: Build and installation tests +# Restrict permissions at top-level to 'read' as a minimal explicit permission. +permissions: + contents: read + on: push: branches: ['master', 'devel'] @@ -21,9 +25,9 @@ jobs: python-version: ["3.12", "3.13"] fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Build and install @@ -46,21 +50,16 @@ jobs: # Add multiple Python versions here to run tests on new(er) versions. python-version: ["3.12", "3.13"] fail-fast: false - # Step 1 to make GUIs work, see https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html - env: - DISPLAY: ':99.0' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - # Step 2 to make GUIs work - - uses: tlambert03/setup-qt-libs@v1 + - name: Setup headless display + uses: pyvista/setup-headless-display-action@v3 - name: Build and install run: | - # Step 3 to make GUIs work - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX # Install same requirements as to be used during regression testing source $CONDA/etc/profile.d/conda.sh conda create -n my_env python=${{ matrix.python-version }} diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index 3dbf0c16..72b931c1 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -22,9 +22,9 @@ jobs: # Add the Python versions here to run tests on new(er) versions. python-version: ["3.13"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -45,9 +45,9 @@ jobs: # Add multiple Python versions here to run tests on new(er) versions. python-version: ["3.13"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/housekeeping.yml b/.github/workflows/housekeeping.yml index 441ea326..77a71813 100644 --- a/.github/workflows/housekeeping.yml +++ b/.github/workflows/housekeeping.yml @@ -18,7 +18,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v5 + - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: > diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 94f23ef6..e9e14597 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -18,9 +18,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: '3.x' - name: Install dependencies @@ -30,7 +30,7 @@ jobs: - name: Build package run: python -m build - name: Store the distribution packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: python-package-distributions path: dist/ @@ -47,7 +47,7 @@ jobs: id-token: write # IMPORTANT: mandatory for trusted publishing steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: python-package-distributions path: dist/ diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 73fae953..c375ba14 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -1,6 +1,11 @@ # This workflow will run some regression tests. name: Regression Tests +# Restrict permissions at top-level to 'read' as a minimal explicit permission. +# This will apply to all jobs that do not define their own permissions. +permissions: + contents: read + on: push: branches: ['master', 'devel'] @@ -23,34 +28,27 @@ jobs: matrix: # Add multiple Python versions here to run tests on new(er) versions. python-version: ["3.13"] - # Step 1 to make GUIs work, see https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html - env: - DISPLAY: ':99.0' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - # Step 2 to make GUIs work - - uses: tlambert03/setup-qt-libs@v1 - - name: Install dependencies + - name: Setup headless display + uses: pyvista/setup-headless-display-action@v3 + - name: Build and install run: | - # Step 3 to make GUIs work - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX # Install same requirements as to be used during regression testing source $CONDA/etc/profile.d/conda.sh - conda config --add channels conda-forge - conda config --set channel_priority strict - conda create -n pytest_env python=${{ matrix.python-version }} - conda activate pytest_env - conda install -y -q --file ./tests/list_of_packages.txt || true - # Install the package itself to make sure that all imports work. + conda create -n my_env python=${{ matrix.python-version }} + conda activate my_env + conda install -y -q -c conda-forge --file ./tests/list_of_packages.txt || true + # Install with -e (in editable mode) to allow the tracking of the test coverage pip install -e .[extras,test] - name: Analysing the code with pytest run: | source $CONDA/etc/profile.d/conda.sh - conda activate pytest_env + conda activate my_env # Run the actual testing of the code with pytest # Using python -m pytest is necessary because pytest has the habit of not looking in the site-packages of the venv python -m pytest -v --basetemp=./tmp -k 'test_unittests or test_gui or test_integration_public' --cov=loadskernel --cov=modelviewer --cov=loadscompare --junitxml=testresult.xml @@ -58,8 +56,8 @@ jobs: coverage report coverage xml -o coverage.xml coverage html --directory ./coverage - - name: Upload test restults and coverage as an artifact - uses: actions/upload-artifact@v4 + - name: Upload test results and coverage as an artifact + uses: actions/upload-artifact@v7 with: name: test results and coverage path: | @@ -69,28 +67,28 @@ jobs: if-no-files-found: ignore Jupyter: - # Building the Jupyter book is not really a regression test. However, it has to be in this workflow due to the handling of - # the artifacts. + # Building the Jupyter book is not really a regression test, but it makes sure that the notebooks are still working. + # In addition, it has to be in this workflow due to the handling of the artifacts. runs-on: ubuntu-latest strategy: matrix: # Select Python version to be used for compiling here. python-version: ["3.13"] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version: 18.x - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies + - name: Build and install run: | python -m pip install --upgrade pip # Install the package itself to make sure that all imports work. pip install .[test] - - name: Assemble the tutorials to a jupyter book and build htlm pages + - name: Assemble the tutorials to a jupyter book and build html pages run: | cd ./doc/tutorials jupyter-book build --execute --html @@ -99,7 +97,7 @@ jobs: mkdir ./doc/html mv ./doc/tutorials/_build/html ./doc/html/tutorials - name: Upload Jupyter book as an artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: tutorials path: ./doc/html @@ -110,7 +108,7 @@ jobs: # Add a dependency to the build job needs: [Jupyter, Pytest] steps: - - uses: actions/download-artifact@v4.1.7 + - uses: actions/download-artifact@v8 with: merge-multiple: true - name: See what we've got and merge artifacts @@ -121,7 +119,7 @@ jobs: mv ./coverage ./pages/coverage - name: Upload artifact for pages # This is not a normal artifact but one that can be deployed to the GitHub pages in the next step - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: name: github-pages # This name may not be changed according to the documentation path: ./pages # There must be only one path @@ -142,7 +140,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup GitHub Pages - uses: actions/configure-pages@v4 + uses: actions/configure-pages@v6 - name: Deploy to Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/AUTHORS.md b/AUTHORS.md index 64480a32..f2590754 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -16,6 +16,7 @@ Schulze, Matthias, Institute of Aeroelasticity, Deutsches Zentrum für Luft- und Handojo, Vega, Institute of Aeroelasticity, Deutsches Zentrum für Luft- und Raumfahrt e.V. Baier, Jan, Institute of Aeroelasticity, Deutsches Zentrum für Luft- und Raumfahrt e.V. Carvalho, Francisco, Institute of Aeroelasticity, Deutsches Zentrum für Luft- und Raumfahrt e.V. +Chang Xu, Visonary Aircraft Concepts, Bauhaus Luftfahrt e.V. ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 2714e07d..3017d2ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Note New releases are marked in the repository using tags. Simply checkout the master branch for the lastest version or use git checkout if you require a specific release, for example 'git checkout 2022.10'. +# Release 2026.05 +- New interface for Nastran 95 +- CFD simulations with SU2: tested with RANS, found and fixed a bug during splining +- Loads Compare can now plot time histories per subcase +- New tool Response Viewer to plot time histories of aircraft states per subcase (e.g. velocities, rates, angles, alpha, beta, etc.) +- Dropped support for Python versions lower than 3.12 +- Dependencies: removed version limitation of Pandas +- Testing: Maintenance of continuous integration workflows + # Release 2026.01 - Added gust and flutter simulation in the frequency domain based of GAFs obtained from linearized CFD (using SU2 and via pulse simulations). This is work is still very new and therefore needs more testing and/or applications. - Refactoring of 'equations' diff --git a/doc/DMAPs/DMAPforNastran95.alter b/doc/DMAPs/DMAPforNastran95.alter new file mode 100644 index 00000000..37d36c20 --- /dev/null +++ b/doc/DMAPs/DMAPforNastran95.alter @@ -0,0 +1,29 @@ +ID My,Aircraft +$ The modal anaylsis is called SOL 3 in Nastran 95. +SOL 3 +$ Print the DMAP code and insert the output command after line 52. +$ I haven't found an option so specify a filename of the op2 file, +$ in my case the resulting file was named 'none'. So remember to keep +$ track of your filenames when working with multiple mass configurations. +DIAG 14,18 +ALTER 52 +OUTPUT2 KGG,MGG,GM,USET,//0/11////*MSC* $ +ENDALTER +CEND +$ +METHOD=100 +ECHO=NONE +$ +BEGIN BULK +PARAM GRDPNT 0 +$ Calculate all modes between 0.1 and 15.0 Hz using the modified Givens method. +$ Because we only want the system matrices, the modal analysis is (stricly speaking) +$ not necessary but allows to compare eigenvalues calculated in Loads Kernel with +$ those found by Nastran 95 as a cross-check. +$------><------><------><------><------><------><------><------><------> +EIGR 100 MGIV 0.1 15.0 +$ +$ Insert structural model below here (include statements are not supported by Nastran 95). +$ +$ Explicitly terminate file. +ENDDATA \ No newline at end of file diff --git a/scripts/DMAPforSOL103.alter b/doc/DMAPs/DMAPforSOL103.alter similarity index 100% rename from scripts/DMAPforSOL103.alter rename to doc/DMAPs/DMAPforSOL103.alter diff --git a/scripts/DMAPforSOL103viaHDF5.alter b/doc/DMAPs/DMAPforSOL103viaHDF5.alter similarity index 100% rename from scripts/DMAPforSOL103viaHDF5.alter rename to doc/DMAPs/DMAPforSOL103viaHDF5.alter diff --git a/scripts/DMAPforSOL145_SOL146.alter b/doc/DMAPs/DMAPforSOL145_SOL146.alter similarity index 100% rename from scripts/DMAPforSOL145_SOL146.alter rename to doc/DMAPs/DMAPforSOL145_SOL146.alter diff --git a/doc/jcl_template.py b/doc/jcl_template.py index b1e6a9a9..b7b07b71 100755 --- a/doc/jcl_template.py +++ b/doc/jcl_template.py @@ -51,20 +51,25 @@ def __init__(self): # bdf file with GRID-cards, 1st file -> 1st monstation 'filename_monstations': ['monstation_MON1.bdf', 'monstation_MON2.bdf'], # The following matrices are required for some mass methods. However, the stiffness is geometry - # and not mass dependent. Overview: - # KGG via DMAP Alter (.op4 or .h5) - required for mass method = 'modalanalysis', 'guyan' or 'B2000' - # GM via DMAP Alter (.op4 or .h5) - required for mass method = 'modalanalysis', 'guyan' - # USET via DMAP Alter and OP2 - required for mass method = 'modalanalysis', 'guyan' - # bdf file(s) with ASET1-card - required for mass method = 'guyan' - # matrix R_trans from B2000 - required for mass method = 'B2000' + # and not mass dependent and is organized here for better overview. You can gerente these matrices + # with a DMAP Alter, see the scripts folder. + # Overview: + # KGG via DMAP Alter (.op4, .h5, .csv) - required for mass method Nastran or B2000 + # GM via DMAP Alter (.op4 or .h5) - required for mass method Nastran + # USET via DMAP Alter (.op2) - required for mass method Nastran + # R_trans from B2000 - required for mass method B2000 + # bdf file(s) with ASET1-card - required Guyan reduction # The HDF5 file format is preferred over OP4 due to better performance and higher precision. Because the # uset is a table, not a matrix, it can't be included in the HDF5 file and still needs to be given as OP2. 'filename_h5': 'SOL103.mtx.h5', 'filename_KGG': 'KGG.dat', 'filename_GM': 'GM.dat', 'filename_uset': 'uset.op2', - 'filename_aset': 'aset.bdf', 'filename_Rtrans': 'Rtrans.csv', + 'filename_aset': 'aset.bdf', + # If you really want to, the matrices Kgg and GM may also be given in OP2 format, e.g. when using + # Nastran 95. Note that the OP2 file is binary so that it is difficult to check its content. + 'filename_op2': 'matrices_from_Nastran95.op2', } # Settings for the aerodynamic model self.aero = {'method': 'mona_steady', @@ -147,15 +152,25 @@ def __init__(self): 'filename_splinegrid': ['splinegrid.bdf'] } # Settings for the structural dynamics. - self.mass = {'method': 'modalanalysis', # Inplemented interfaces: 'f06', 'modalanalysis', 'guyan', 'CoFE', 'B2000' + self.mass = {'method': 'modalanalysis', + # 'modalanalysis', 'guyan' - Eigenvalue / -vector analysis, optionally incl. Guyan reduction, + # based on system matrices obtained from Nastran (MSC or Siemens NX) + # 'f06' - Eigenvalues and -vectors calculated with SOL 103 and parsed from f06-file + # 'CoFE' - Matrices from Nastran Compatible Finite Elements (CoFE) + # 'B2000' - Matrices from DLR's version of B2000++ + # 'Nastran95' - Matrices from open-source NASA Structural Analysis System (Nastran 95) + # Note that the the stiffness is geometry dependent and is therfor given in the geom setion. All mass + # cases share the same geometry and stiffness matrix. 'key': ['M1', 'M2'], - # MGG via DMAP Alter and HDF5 - 'filename_h5': ['SOL103_M1.mtx.h5', 'SOL103_M2.mtx.h5'], # MGG via DMAP Alter and OP4 'filename_MGG': ['MGG_M1.dat', 'MGG_M2.dat'], + # MGG via DMAP Alter and HDF5 + 'filename_h5': ['SOL103_M1.mtx.h5', 'SOL103_M2.mtx.h5'], + # MGG via DMAP Alter and OP2 + 'filename_op2': ['MGG_M1.op2', 'MGG_M2.op2'], # eigenvalues and eigenvectors from .f06-file - required for 'mona' 'filename_S103': ['SOL103_M1.f06', 'SOL103_M1.f06'], - # eigenvalues and eigenvectors from .f06-file - required for 'mona' + # eigenvalues and eigenvectors from .mat-file - required for 'CoFe' 'filename_CoFE': ['M1.mat', 'M2.mat'], # True or False, omits first six modes 'omit_rb_modes': False, diff --git a/doc/tutorials/DC3_model/JCLs/jcl_dc3_Nastran95.py b/doc/tutorials/DC3_model/JCLs/jcl_dc3_Nastran95.py new file mode 100755 index 00000000..008ca9c4 --- /dev/null +++ b/doc/tutorials/DC3_model/JCLs/jcl_dc3_Nastran95.py @@ -0,0 +1,250 @@ +""" +Job Control File documentation +The Job Control (jcl) is a python class which defines the model and and the simulation and is imported at +the beginning of every simulation. Unlike a conventional parameter file, this allows scripting/programming +of the input, e.g. to convert units, generate mutiple load cases, etc. +Note that this documentation of parameters is comprehensive, but a) not all parameters are necessary for +every kind of simulation and b) some parameters are for experts only --> your JCL might be much smaller. +""" +import numpy as np +import os +from loadskernel.units import ft2m, tas2Ma +from loadskernel import jcl_helper +import pathlib + + +class jcl: + + def __init__(self): + + model_root = pathlib.Path(__file__).parent.parent.resolve() + + # Give your aircraft a name and set some general parameters + self.general = {'aircraft': 'DC3', + # Reference span width (from tip to tip) + 'b_ref': 29.0, + # Reference chord length + 'c_ref': 3.508, + # Reference area + 'A_ref': 91.7, + # Mean aerodynamic center, also used as moments reference point + 'MAC_ref': [8.566, 0.0, 0.0], + } + """ + The electronic flight control system (EFCS) provides the "wireing" of the pilot commands + xi, eta and zeta with the control surface deflections. This is aircraft-specific and needs + to be implemented as a python module. + """ + # Electronic flight control system + self.efcs = {'version': 'efcs_dc3', # Name of the corresponding python module + # Path where to find the EFCS module + 'path': os.path.join(model_root, 'efcs'), + } + # Read the structural geometry + self.geom = {'method': 'mona', # ModGen and/or Nastran (mona) BDFs + # bdf file(s) with GRIDs and CORDs (CORD1R and CORD2R) + 'filename_grid': [os.path.join(model_root, 'fem', 'structure_only.bdf')], + # bdf file(s) with CQUADs and CTRIAs, for visualization only, e.g. outer skin on the aircraft + # 'filename_shell': [], + # bdf file(s) with MONPNT-cards + 'filename_monpnt': os.path.join(model_root, 'fem', 'export_monitoring-stations.csv'), + # Specify the OP2 file containing the matrices Kgg, GM and the uset. + 'filename_op2': os.path.join(model_root, 'fem', 'Nast95_structure_only.op2'), + } + # Settings for the aerodynamic model + self.aero = {'method': 'mona_steady', + # 'mona_steady' - steady trim and quasi-steady time domain simulations + # 'mona_unsteady' - unsteady time domain simulation based on the RFA, e.g. for gust + # 'freq_dom' - frequency domain simulations, e.g. gust, continuous turbulence, flutter, etc + # 'nonlin_steady' - steady trim and quasi-steady time domain simulations with some non-linearities + # 'cfd_steady' - steady trim + # 'cfd_unsteady' - unsteady time domain simulation, e.g. for gust + # + # True or False, aerodynamic feedback of elastic structure on aerodynamics can be deactivated. + # You will still see deformations, but there is no coupling. + 'flex': True, + # aerogrid is given by CAERO1, CAERO7 or by CQUAD4 cards + 'method_caero': 'CAERO1', + # bdf file(s) with CAERO1 or CQUAD4-cards for aerogrid. IDs in ascending order. + 'filename_caero_bdf': [os.path.join(model_root, 'aero', 'vt', 'vt.CAERO1'), + os.path.join(model_root, 'aero', 'left-ht', 'left-ht.CAERO1'), + os.path.join(model_root, 'aero', 'right-ht', 'right-ht.CAERO1'), + os.path.join(model_root, 'aero', 'left-wing', 'left-wing.CAERO1'), + os.path.join(model_root, 'aero', 'right-wing', 'right-wing.CAERO1')], + # DMI Matrix for camber and twist correction. Same order as the aerogrid. + 'filename_DMI_W2GJ': [os.path.join(model_root, 'fem', 'w2gj_list.DMI_merge')], + # bdf file(s) with AESURF-cards + 'filename_aesurf': [os.path.join(model_root, 'aero', 'vt', 'vt.AESURF'), + os.path.join(model_root, 'aero', 'left-ht', 'left-ht.AESURF'), + os.path.join(model_root, 'aero', 'right-ht', 'right-ht.AESURF'), + os.path.join(model_root, 'aero', 'left-wing', 'left-wing.AESURF'), + os.path.join(model_root, 'aero', 'right-wing', 'right-wing.AESURF') + ], + # bdf file(s) with AELIST-cards + 'filename_aelist': [os.path.join(model_root, 'aero', 'vt', 'vt.AELIST'), + os.path.join(model_root, 'aero', 'left-ht', 'left-ht.AELIST'), + os.path.join(model_root, 'aero', 'right-ht', 'right-ht.AELIST'), + os.path.join(model_root, 'aero', 'left-wing', 'left-wing.AELIST'), + os.path.join(model_root, 'aero', 'right-wing', 'right-wing.AELIST') + ], + # The hingeline of a CS is given by a CORD. Either the y- or the z-axis is taken as hingeline. 'y', 'z' + 'hingeline': 'y', + # 'vlm' (panel-aero), 'dlm' (panel-aero) or 'nastran' (external form matrices) + 'method_AIC': 'vlm', + 'key': ['VC', 'VD'], + 'Ma': [0.27, 0.34], + } + # Set the was in which the aerodynamic forces are applied to the structure. + self.spline = {'method': 'nearest_neighbour', # Options: 'nearest_neighbour', 'rbf' or 'nastran' + # Possibility to use only a subset of the structural grid for splining. True or False + 'splinegrid': False, + # bdf file(s) with GRIDs to ne used + 'filename_splinegrid': ['splinegrid.bdf'] + } + # Settings for the structural dynamics. + self.mass = {'method': 'Nastran95', # Use new Nastran 95 interface + 'key': ['M3'], + # MGG via DMAP Alter and OP2 + 'filename_op2': [os.path.join(model_root, 'fem', 'Nast95_M3.op2')], + # True or False, omits first six modes + 'omit_rb_modes': True, + # list(s) of modes to use + 'modes': [np.arange(1, 71), np.arange(1, 71), np.arange(1, 71), np.arange(1, 71)], + } + # Modal damping can be applied as a factor of the stiffness matrix. + self.damping = {'method': 'modal', + 'damping': 0.02, + } + # The international standard atmosphere (ISA) + self.atmo = {'method': 'ISA', + 'key': ['FL000', 'FL055', 'FL075', 'FL210'], + # Altitude in meters + 'h': ft2m([0, 5500, 7500, 21000,]), + } + # Setting of the rigid body equations of motion + self.eom = {'version': 'waszak'} # 'linear' or 'waszak' + + """ + This section controls the automatic plotting and selection of dimensioning load cases. + Simply put a list of names of the monitoring stations (e.g. ['MON1', 'MON2',...]) into the dictionary + of possible load plots listed below. This will generate a pdf document and nastran force and moment + cards for the dimensioning load cases. + """ + self.loadplots = {'potatos_fz_mx': [], + 'potatos_mx_my': ['WL01', 'WL03', 'WL05', 'WL07', 'WL09', 'WL11', 'WL13', 'WL15', 'WL17', + 'WL19', 'WL21', 'WL23', 'WL25', 'WL27', 'WL29', 'WL31', 'WR31', 'WR29', + 'WR27', 'WR25', 'WR23', 'WR21', 'WR19', 'WR17', 'WR15', 'WR13', 'WR11', + 'WR09', 'WR07', 'WR05', 'WR03', 'WR01'], + 'potatos_fz_my': [], + 'potatos_fy_mx': [], + 'potatos_mx_mz': [], + 'potatos_my_mz': [], + 'cuttingforces_wing': ['WL01', 'WL03', 'WL05', 'WL07', 'WL09', 'WL11', 'WL13', 'WL15', + 'WL17', 'WL19', 'WL21', 'WL23', 'WL25', 'WL27', 'WL29', 'WL31', + 'WR31', 'WR29', 'WR27', 'WR25', 'WR23', 'WR21', 'WR19', 'WR17', + 'WR15', 'WR13', 'WR11', 'WR09', 'WR07', 'WR05', 'WR03', 'WR01'], + } + """ + The trimcase defines the maneuver load case, one dictionary per load case. + There may be hundreds or thousands of load cases, so at some point it might be beneficial to script this section or + import an excel sheet. + """ + self.trimcase = [{'desc': 'CC.M3.OVCFL000.level', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': '', + # Subcase ID number, for Nastran in acending order + 'subcase': 1, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': 1.0, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0 / 180.0 * np.pi, + # Pitch rate in rad/s + 'q': 0.0 / 180.0 * np.pi, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + }, + {'desc': 'CC.M3.OVCFL000.pushdown', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': '', + # Subcase ID number, for Nastran in acending order + 'subcase': 2, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': -1.0, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0 / 180.0 * np.pi, + # Pitch rate in rad/s + 'q': 0.0 / 180.0 * np.pi, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + }, + {'desc': 'CC.M3.OVCFL000.pullup', # Descriptive string of the maneuver case + # Kind of trim condition, blank for trim about all three axes, for more trim conditions see + # trim_conditions.py + 'maneuver': '', + # Subcase ID number, for Nastran in acending order + 'subcase': 3, + # Setting of the operational point + # The flight speed is given by the Mach number + 'Ma': tas2Ma(70.0, 0.0), + # Aero key + 'aero': 'VC', + # Atmo key + 'altitude': 'FL000', + # Mass key + 'mass': 'M3', + # Load factor Nz + 'Nz': 2.5, + # Velocities and accelerations given in ISO 9300 coordinate system (right-handed, forward-right-down) + # Roll rate in rad/s + 'p': 0.0, + # Pitch rate in rad/s + 'q': 0.0, + # Yaw rate in rad/s + 'r': 0.0, + # Roll acceleration in rad/s^2 + 'pdot': 0.0, + # Pitch acceleration in rad/s^2 + 'qdot': 0.0, + # Yaw acceleration in rad/s^2 + 'rdot': 0.0, + }] + """ + For every trimcase, a corresponding simcase is required. For maneuvers, it may be empty self.simcase = [{}]. + A time simulation is triggered if the simcase contains at least 'dt' and 't_final' + """ + self.simcase = jcl_helper.generate_empty_listofdicts(self.trimcase) + # End diff --git a/doc/tutorials/DC3_model/fem/Nast95_M3.op2 b/doc/tutorials/DC3_model/fem/Nast95_M3.op2 new file mode 100644 index 00000000..abcb4f83 Binary files /dev/null and b/doc/tutorials/DC3_model/fem/Nast95_M3.op2 differ diff --git a/doc/tutorials/DC3_model/fem/Nast95_structure_only.op2 b/doc/tutorials/DC3_model/fem/Nast95_structure_only.op2 new file mode 100644 index 00000000..c27ffc94 Binary files /dev/null and b/doc/tutorials/DC3_model/fem/Nast95_structure_only.op2 differ diff --git a/doc/tutorials/DC3_model/fem/Nastran95/Nast95_M3.bdf b/doc/tutorials/DC3_model/fem/Nastran95/Nast95_M3.bdf new file mode 100644 index 00000000..eb8072a1 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/Nastran95/Nast95_M3.bdf @@ -0,0 +1,824 @@ +ID DC3,Aircraft +SOL 3 +DIAG 14,18 +ALTER 52 +OUTPUT2 MGG,,,,//0/11////*MSC* $ +ENDALTER +CEND +$ +METHOD=100 +ECHO=NONE +$ +BEGIN BULK +PARAM GRDPNT 0 +$------><------><------><------><------><------><------><------><------> +EIGR 100 MGIV 0.1 15.0 +$------><------><------><------><------><------><------><------><------> +$ +CBAR 3328001 33280013329000133290002 0.0 1. 0.0 +CBAR 3328002 33280023329000233290003 0.0 1. 0.0 +CBAR 3328003 33280033329000333290004 0.0 1. 0.0 +CBAR 3328004 33280043329000433290005 0.0 1. 0.0 +CBAR 3328005 33280053329000533290006 0.0 1. 0.0 +CBAR 3328006 33280063329000633290007 0.0 1. 0.0 +CBAR 3328007 33280073329000733290008 0.0 1. 0.0 +CBAR 3328008 33280083329000833290009 0.0 1. 0.0 +CBAR 3338001 33380013339000133390002 0.0 0.0 1. +CBAR 3338002 33380023339000233390003 0.0 0.0 1. +CBAR 3338003 33380033339000333390004 0.0 0.0 1. +CBAR 3338004 33380043339000433390005 0.0 0.0 1. +CBAR 3338005 33380053339000533390006 0.0 0.0 1. +CBAR 3338006 33380063339000633390007 0.0 0.0 1. +CBAR 3338007 33380073339000733390008 0.0 0.0 1. +CBAR 3348001 33480013349000133490002 0.0 0.0 1. +CBAR 3348002 33480023349000233490003 0.0 0.0 1. +CBAR 3348003 33480033349000333490004 0.0 0.0 1. +CBAR 3348004 33480043349000433490005 0.0 0.0 1. +CBAR 3348005 33480053349000533490006 0.0 0.0 1. +CBAR 3348006 33480063349000633490007 0.0 0.0 1. +CBAR 3348007 33480073349000733490008 0.0 0.0 1. +CBAR 5408001 54080015409000154090002 0.0 0.0 1. +CBAR 5408002 54080025409000254090003 0.0 0.0 1. +CBAR 5408003 54080035409000354090004 0.0 0.0 1. +CBAR 5408004 54080045409000454090005 0.0 0.0 1. +CBAR 5408005 54080055409000554090006 0.0 0.0 1. +CBAR 5408006 54080065409000654090007 0.0 0.0 1. +CBAR 5408007 54080075409000754090008 0.0 0.0 1. +CBAR 5408008 54080085409000854090009 0.0 0.0 1. +CBAR 5408009 54080095409000954090010 0.0 0.0 1. +CBAR 5408010 54080105409001054090011 0.0 0.0 1. +CBAR 5408011 54080115409001154090012 0.0 0.0 1. +CBAR 5408012 54080125409001254090013 0.0 0.0 1. +CBAR 5408013 54080135409001354090014 0.0 0.0 1. +CBAR 5408014 54080145409001454090015 0.0 0.0 1. +CBAR 5408015 54080155409001554090016 0.0 0.0 1. +CBAR 5408016 54080165409001654090017 0.0 0.0 1. +CBAR 5408017 54080175409001754090018 0.0 0.0 1. +CBAR 5408018 54080185409001854090019 0.0 0.0 1. +CBAR 5408019 54080195409001954090020 0.0 0.0 1. +CBAR 5408020 54080205409002054090021 0.0 0.0 1. +CBAR 5408021 54080215409002154090022 0.0 0.0 1. +CBAR 5408022 54080225409002254090023 0.0 0.0 1. +CBAR 5408023 54080235409002354090024 0.0 0.0 1. +CBAR 5408024 54080245409002454090025 0.0 0.0 1. +CBAR 5408025 54080255409002554090026 0.0 0.0 1. +CBAR 5408026 54080265409002654090027 0.0 0.0 1. +CBAR 5408027 54080275409002754090028 0.0 0.0 1. +CBAR 5408028 54080285409002854090029 0.0 0.0 1. +CBAR 5408029 54080295409002954090030 0.0 0.0 1. +CBAR 5408030 54080305409003054090031 0.0 0.0 1. +CBAR 6408001 64080016409000164090002 0.0 0.0 1. +CBAR 6408002 64080026409000264090003 0.0 0.0 1. +CBAR 6408003 64080036409000364090004 0.0 0.0 1. +CBAR 6408004 64080046409000464090005 0.0 0.0 1. +CBAR 6408005 64080056409000564090006 0.0 0.0 1. +CBAR 6408006 64080066409000664090007 0.0 0.0 1. +CBAR 6408007 64080076409000764090008 0.0 0.0 1. +CBAR 6408008 64080086409000864090009 0.0 0.0 1. +CBAR 6408009 64080096409000964090010 0.0 0.0 1. +CBAR 6408010 64080106409001064090011 0.0 0.0 1. +CBAR 6408011 64080116409001164090012 0.0 0.0 1. +CBAR 6408012 64080126409001264090013 0.0 0.0 1. +CBAR 6408013 64080136409001364090014 0.0 0.0 1. +CBAR 6408014 64080146409001464090015 0.0 0.0 1. +CBAR 6408015 64080156409001564090016 0.0 0.0 1. +CBAR 6408016 64080166409001664090017 0.0 0.0 1. +CBAR 6408017 64080176409001764090018 0.0 0.0 1. +CBAR 6408018 64080186409001864090019 0.0 0.0 1. +CBAR 6408019 64080196409001964090020 0.0 0.0 1. +CBAR 6408020 64080206409002064090021 0.0 0.0 1. +CBAR 6408021 64080216409002164090022 0.0 0.0 1. +CBAR 6408022 64080226409002264090023 0.0 0.0 1. +CBAR 6408023 64080236409002364090024 0.0 0.0 1. +CBAR 6408024 64080246409002464090025 0.0 0.0 1. +CBAR 6408025 64080256409002564090026 0.0 0.0 1. +CBAR 6408026 64080266409002664090027 0.0 0.0 1. +CBAR 6408027 64080276409002764090028 0.0 0.0 1. +CBAR 6408028 64080286409002864090029 0.0 0.0 1. +CBAR 6408029 64080296409002964090030 0.0 0.0 1. +CBAR 6408030 64080306409003064090031 0.0 0.0 1. +CONM2 8000154090001 0 161.473.0145699-.263119-.156335 +1 ++1 14.7777 -5.6-14 92.9288 .30998 4.39-16 100.509 +CONM2 8000254090003 0 161.466.0145699 .263094-.156336 +2 ++2 14.7761 3.98-6 92.9245 .30996 4.37-5 100.502 +CONM2 8000354090003 0 161.473.0145699 -.26311-.156335 +3 ++3 14.7777 7.97-6 92.9291 .30998 8.74-5 100.509 +CONM2 8000454090004 0 160.344.0145699 -.26126-.156335 +4 ++4 14.5198 7.919-6 92.2792 .30781 8.679-5 99.652 +CONM2 9000164090001 0 161.473.0145699 .263119-.156335 +5 ++5 14.7777 5.67-14 92.9288 -.30998 -4.3-16 100.509 +CONM2 9000264090003 0 161.466.0145699 -.26309-.156336 +6 ++6 14.7761 -3.98-6 92.9245 -.30996 -4.37-5 100.502 +CONM2 9000364090003 0 161.473.0145699 .26311-.156335 +7 ++7 14.7777 -7.97-6 92.9291 -.30998 -8.74-5 100.509 +CONM2 9000464090004 0 160.344.0145699 .26126-.156335 +8 ++8 14.5198 -7.91-6 92.2792 -.30781 -8.67-5 99.652 +CONM2 110001 100001 0 19.313 0.0 0.0 0.0 +9 ++9 .358 0.0 0.0 0.0 0.0 0.0 +CONM2 110002 100002 0 141.008 0.0 0.0 0.0 +10 ++10 139.474 0.0 0.0 0.0 0.0 0.0 +CONM2 110003 100003 0 172.413 0.0 0.0 0.0 +11 ++11 254.959 0.0 0.0 0.0 0.0 0.0 +CONM2 110004 100004 0 184.19 0.0 0.0 0.0 +12 ++12 310.855 0.0 0.0 0.0 0.0 0.0 +CONM2 110005 100005 0 180.264 0.0 0.0 0.0 +13 ++13 291.4 0.0 0.0 0.0 0.0 0.0 +CONM2 110006 100006 0 178.301 0.0 0.0 0.0 +14 ++14 281.984 0.0 0.0 0.0 0.0 0.0 +CONM2 110007 100007 0 162.599 0.0 0.0 0.0 +15 ++15 213.852 0.0 0.0 0.0 0.0 0.0 +CONM2 110008 100008 0 146.896 0.0 0.0 0.0 +16 ++16 157.686 0.0 0.0 0.0 0.0 0.0 +CONM2 110009 100009 0 117.454 0.0 0.0 0.0 +17 ++17 80.606 0.0 0.0 0.0 0.0 0.0 +CONM2 110010 100010 0 74.272 0.0 0.0 0.0 +18 ++18 20.382 0.0 0.0 0.0 0.0 0.0 +CONM2 110011 100011 0 31.09 0.0 0.0 0.0 +19 ++19 1.495 0.0 0.0 0.0 0.0 0.0 +CONM2 12000164090006 0 139.815 -.20038 .2463-.197264 +20 ++20 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 12000254090006 0 139.815 -.20038 -.2463-.197264 +21 ++21 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 120003 100010 0 31.07 -1.0209 0.0 -.672 +22 ++22 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 121001 100004 0 425.47 -.1293 0.0 -1.55 +23 ++23 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 13000164090005 0 103.55 .01962 -.23426-.197264 +24 ++24 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 13000254090005 0 103.55 .01962 .23426-.197264 +25 ++25 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 140001 100003 0 241.9 -.7482 0.0 -.123 +26 ++26 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 150001 100003 0 12.5 -.7482 0.0 -.123 +27 ++27 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 160001 100002 0 142.3 -.6501 0.0 -1.55 +28 ++28 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 170001 100002 0 255.9 -.6501 0.0 -1.55 +29 ++29 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 180001 100004 0 608. -.7173 0.0 -1.55 +30 ++30 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 190001 100004 0 696.3 .1357 0.0 -1.55 +31 ++31 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 707001 100003 0 746. -.5842 0.0 -1.55 +32 ++32 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 707002 100005 0 1543. .3556 0.0 -1.55 +33 ++33 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 707003 100008 0 231. -.6477 0.0 -1.55 +34 ++34 0.0 0.0 0.0 0.0 0.0 0.0 +CONM2 332810133290001 0 13.375 0.0 0.0 0.0 +35 ++35 0.0 0.0 0.0 0.0 0.0 20.8867 +CONM2 332810233290002 0 23.065 0.0 0.0 0.0 +36 ++36 0.0 0.0 0.0 0.0 0.0 31.0587 +CONM2 332810333290003 0 19.654 0.0 0.0 0.0 +37 ++37 0.0 0.0 0.0 0.0 0.0 22.5508 +CONM2 332810433290004 0 16.515 0.0 0.0 0.0 +38 ++38 0.0 0.0 0.0 0.0 0.0 15.9235 +CONM2 332810533290005 0 13.65 0.0 0.0 0.0 +39 ++39 0.0 0.0 0.0 0.0 0.0 10.8769 +CONM2 332810633290006 0 11.057 0.0 0.0 0.0 +40 ++40 0.0 0.0 0.0 0.0 0.0 7.1371 +CONM2 332810733290007 0 8.737 0.0 0.0 0.0 +41 ++41 0.0 0.0 0.0 0.0 0.0 4.4562 +CONM2 332810833290008 0 6.69 0.0 0.0 0.0 +42 ++42 0.0 0.0 0.0 0.0 0.0 2.6126 +CONM2 332810933290009 0 2.458 0.0 0.0 0.0 +43 ++43 0.0 0.0 0.0 0.0 0.0 .7053 +CONM2 333810133390001 0 10.663 0.0 0.0 0.0 +44 ++44 0.0 0.0 8.6823 0.0 0.0 0.0 +CONM2 333810233390002 0 17.841 0.0 0.0 0.0 +45 ++45 0.0 0.0 12.5584 0.0 0.0 0.0 +CONM2 333810333390003 0 14.466 0.0 0.0 0.0 +46 ++46 0.0 0.0 8.5231 0.0 0.0 0.0 +CONM2 333810433390004 0 11.445 0.0 0.0 0.0 +47 ++47 0.0 0.0 5.3348 0.0 0.0 0.0 +CONM2 333810533390005 0 8.777 0.0 0.0 0.0 +48 ++48 0.0 0.0 3.1377 0.0 0.0 0.0 +CONM2 333810633390006 0 6.463 0.0 0.0 0.0 +49 ++49 0.0 0.0 1.7012 0.0 0.0 0.0 +CONM2 333810733390007 0 4.502 0.0 0.0 0.0 +50 ++50 0.0 0.0 .8284 0.0 0.0 0.0 +CONM2 333810833390008 0 1.442 0.0 0.0 0.0 +51 ++51 0.0 0.0 .1707 0.0 0.0 0.0 +CONM2 334810133490001 0 10.663 0.0 0.0 0.0 +52 ++52 0.0 0.0 8.6823 0.0 0.0 0.0 +CONM2 334810233490002 0 17.841 0.0 0.0 0.0 +53 ++53 0.0 0.0 12.5584 0.0 0.0 0.0 +CONM2 334810333490003 0 14.466 0.0 0.0 0.0 +54 ++54 0.0 0.0 8.5231 0.0 0.0 0.0 +CONM2 334810433490004 0 11.445 0.0 0.0 0.0 +55 ++55 0.0 0.0 5.3348 0.0 0.0 0.0 +CONM2 334810533490005 0 8.777 0.0 0.0 0.0 +56 ++56 0.0 0.0 3.1377 0.0 0.0 0.0 +CONM2 334810633490006 0 6.463 0.0 0.0 0.0 +57 ++57 0.0 0.0 1.7012 0.0 0.0 0.0 +CONM2 334810733490007 0 4.502 0.0 0.0 0.0 +58 ++58 0.0 0.0 .8284 0.0 0.0 0.0 +CONM2 334810833490008 0 1.442 0.0 0.0 0.0 +59 ++59 0.0 0.0 .1707 0.0 0.0 0.0 +CONM2 541000154090001 0 59.72 2.49282 5.97-18-.022564 +60 ++60 0.0 0.0 37.9722 0.0 0.0 0.0 +CONM2 541000254090002 0 56.44 2.49282 3.8-5-.022564 +61 ++61 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 541000354090003 0 55.44 2.49282-4.000-5-.022564 +62 ++62 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 541000454090004 0 55.44 2.49282 0.0-.022564 +63 ++63 0.0 0.0 75.6792 0.0 0.0 0.0 +CONM2 541000554090005 0 55.44 2.49282-4.000-5-.022564 +64 ++64 0.0 0.0 66.3853 0.0 0.0 0.0 +CONM2 541000654090006 0 55.44 2.49282 0.0-.022564 +65 ++65 0.0 0.0 75.9448 0.0 0.0 0.0 +CONM2 541000754090007 0 50.44 2.49282 4.-5-.022564 +66 ++66 0.0 0.0 85.2402 0.0 0.0 0.0 +CONM2 541000854090008 0 50.44 2.49282 0.0-.022564 +67 ++67 0.0 0.0 69.5011 0.0 0.0 0.0 +CONM2 541000954090009 0 46.711 2.21047-2.000-5-.021837 +68 ++68 0.0 0.0 55.533 0.0 0.0 0.0 +CONM2 541001054090010 0 40.074 2.34741-1.400-4 -.02121 +69 ++69 0.0 0.0 48.7313 0.0 0.0 0.0 +CONM2 541001154090011 0 37.527 2.27475-1.500-4-.020584 +70 ++70 0.0 0.0 42.5973 0.0 0.0 0.0 +CONM2 541001254090012 0 35.07 2.202 -1.7-4-.019957 +71 ++71 0.0 0.0 37.0827 0.0 0.0 0.0 +CONM2 541001354090013 0 22.702 2.12934 -1.9-4 -.01933 +72 ++72 0.0 0.0 32.1402 0.0 0.0 0.0 +CONM2 541001454090014 0 21.422 2.05659 -3.1-4-.018603 +73 ++73 0.0 0.0 27.7267 0.0 0.0 0.0 +CONM2 541001554090015 0 19.231 1.98383 -3.3-4-.017976 +74 ++74 0.0 0.0 23.7991 0.0 0.0 0.0 +CONM2 541001654090016 0 18.126 1.91118-3.500-4-.017349 +75 ++75 0.0 0.0 20.3172 0.0 0.0 0.0 +CONM2 541001754090017 0 16.108 1.83843-3.900-4-.016721 +76 ++76 0.0 0.0 17.2447 0.0 0.0 0.0 +CONM2 541001854090018 0 15.177 1.76578 -5.2-4-.016093 +77 ++77 0.0 0.0 14.5451 0.0 0.0 0.0 +CONM2 541001954090019 0 13.33 1.69303-5.600-4-.015365 +78 ++78 0.0 0.0 12.1843 0.0 0.0 0.0 +CONM2 541002054090020 0 12.569 1.62038-6.000-4-.014736 +79 ++79 0.0 0.0 10.1313 0.0 0.0 0.0 +CONM2 541002154090021 0 10.891 1.54763 -6.6-4-.014106 +80 ++80 0.0 0.0 8.3566 0.0 0.0 0.0 +CONM2 541002254090022 0 10.297 1.47488-8.100-4-.013477 +81 ++81 0.0 0.0 6.8312 0.0 0.0 0.0 +CONM2 541002354090023 0 8.786 1.40225 -9.-4-.012844 +82 ++82 0.0 0.0 5.5302 0.0 0.0 0.0 +CONM2 541002454090024 0 8.357 1.3295 -9.-4-.012113 +83 ++83 0.0 0.0 4.4292 0.0 0.0 0.0 +CONM2 541002554090025 0 7.01 1.25678 -1.1-3-.011473 +84 ++84 0.0 0.0 3.5048 0.0 0.0 0.0 +CONM2 541002654090026 0 6.744 1.18415 -1.2-3-.010841 +85 ++85 0.0 0.0 2.7359 0.0 0.0 0.0 +CONM2 541002754090027 0 5.558 1.11141 -1.2-3-.010206 +86 ++86 0.0 0.0 2.1038 0.0 0.0 0.0 +CONM2 541002854090028 0 4.453 1.03871-1.500-3-9.562-3 +87 ++87 0.0 0.0 1.5901 0.0 0.0 0.0 +CONM2 541002954090029 0 4.426 .96601 -1.7-3-8.915-3 +88 ++88 0.0 0.0 1.1782 0.0 0.0 0.0 +CONM2 541003054090030 0 3.478 .8934-2.000-3-8.155-3 +89 ++89 0.0 0.0 .8524 0.0 0.0 0.0 +CONM2 541003154090031 0 1.304 .8204-1.000-4 -7.69-3 +90 ++90 0.0 0.0 .3002 0.0 0.0 0.0 +CONM2 541100154100001 0 287.8 0.0 0.0 0.0 +91 ++91 73.257 0.0 0.0 0.0 0.0 0.0 +CONM2 541100254100002 0 470.95 0.0 0.0 0.0 +92 ++92 102.107 0.0 0.0 0.0 0.0 0.0 +CONM2 541100354100003 0 183.15 0.0 0.0 0.0 +93 ++93 33.408 0.0 0.0 0.0 0.0 0.0 +CONM2 641000164090001 0 59.72 2.49282-5.97-18-.022564 +94 ++94 0.0 0.0 37.9722 0.0 0.0 0.0 +CONM2 641000264090002 0 56.44 2.49282 -3.8-5-.022564 +95 ++95 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 641000364090003 0 55.44 2.492824.0000-5-.022564 +96 ++96 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 641000464090004 0 55.44 2.49282 0.0-.022564 +97 ++97 0.0 0.0 75.6792 0.0 0.0 0.0 +CONM2 641000564090005 0 55.44 2.492824.0000-5-.022564 +98 ++98 0.0 0.0 66.3853 0.0 0.0 0.0 +CONM2 641000664090006 0 55.44 2.49282 0.0-.022564 +99 ++99 0.0 0.0 75.9448 0.0 0.0 0.0 +CONM2 641000764090007 0 50.44 2.49282 -4.-5-.022564 +100 ++100 0.0 0.0 85.2402 0.0 0.0 0.0 +CONM2 641000864090008 0 50.44 2.49282 0.0-.022564 +101 ++101 0.0 0.0 69.5011 0.0 0.0 0.0 +CONM2 641000964090009 0 46.711 2.21046 -1.-5-.021839 +102 ++102 0.0 0.0 55.533 0.0 0.0 0.0 +CONM2 641001064090010 0 40.074 2.347397.0000-5-.021215 +103 ++103 0.0 0.0 48.7313 0.0 0.0 0.0 +CONM2 641001164090011 0 37.527 2.274736.0000-5-.020591 +104 ++104 0.0 0.0 42.5973 0.0 0.0 0.0 +CONM2 641001264090012 0 35.07 2.20197 4.-5-.019967 +105 ++105 0.0 0.0 37.0827 0.0 0.0 0.0 +CONM2 641001364090013 0 22.702 2.129312.0000-5-.019343 +106 ++106 0.0 0.0 32.1402 0.0 0.0 0.0 +CONM2 641001464090014 0 21.422 2.05655 1.1-4-.018618 +107 ++107 0.0 0.0 27.7267 0.0 0.0 0.0 +CONM2 641001564090015 0 19.231 1.983789.0000-5-.017994 +108 ++108 0.0 0.0 23.7991 0.0 0.0 0.0 +CONM2 641001664090016 0 18.126 1.911127.0000-5 -.01737 +109 ++109 0.0 0.0 20.3172 0.0 0.0 0.0 +CONM2 641001764090017 0 16.108 1.838367.0000-5-.016744 +110 ++110 0.0 0.0 17.2447 0.0 0.0 0.0 +CONM2 641001864090018 0 15.177 1.76571.6000-4 -.01612 +111 ++111 0.0 0.0 14.5451 0.0 0.0 0.0 +CONM2 641001964090019 0 13.33 1.69294 1.5-4-.015395 +112 ++112 0.0 0.0 12.1843 0.0 0.0 0.0 +CONM2 641002064090020 0 12.569 1.62028 1.5-4-.014769 +113 ++113 0.0 0.0 10.1313 0.0 0.0 0.0 +CONM2 641002164090021 0 10.891 1.54752 1.5-4-.014144 +114 ++114 0.0 0.0 8.3566 0.0 0.0 0.0 +CONM2 641002264090022 0 10.297 1.474762.5000-4-.013518 +115 ++115 0.0 0.0 6.8312 0.0 0.0 0.0 +CONM2 641002364090023 0 8.786 1.402123.0000-4-.012889 +116 ++116 0.0 0.0 5.5302 0.0 0.0 0.0 +CONM2 641002464090024 0 8.357 1.32935 2.-4-.012165 +117 ++117 0.0 0.0 4.4292 0.0 0.0 0.0 +CONM2 641002564090025 0 7.01 1.25661 3.-4-.011533 +118 ++118 0.0 0.0 3.5048 0.0 0.0 0.0 +CONM2 641002764090027 0 5.558 1.11122 3.-4-.010273 +119 ++119 0.0 0.0 2.1038 0.0 0.0 0.0 +CONM2 641002864090028 0 4.453 1.038474.0000-4-9.644-3 +120 ++120 0.0 0.0 1.5901 0.0 0.0 0.0 +CONM2 641002964090029 0 4.426 .965755.0000-4-9.004-3 +121 ++121 0.0 0.0 1.1782 0.0 0.0 0.0 +CONM2 641003064090030 0 3.478 .89315.0000-4-8.267-3 +122 ++122 0.0 0.0 .8524 0.0 0.0 0.0 +CONM2 641003164090031 0 1.304 .82041.0000-4 -7.69-3 +123 ++123 0.0 0.0 .3002 0.0 0.0 0.0 +CONM2 641100164100001 0 287.8 0.0 0.0 0.0 +124 ++124 73.257 0.0 0.0 0.0 0.0 0.0 +CONM2 641100264100002 0 470.95 0.0 0.0 0.0 +125 ++125 102.107 0.0 0.0 0.0 0.0 0.0 +CONM2 641100364100003 0 183.15 0.0 0.0 0.0 +126 ++126 33.408 0.0 0.0 0.0 0.0 0.0 +CONM2 410012664090026 0 6.744 1.18395 3.-4-.010907 +127 ++127 0.0 0.0 2.7359 0.0 0.0 0.0 +CORD2R 3329001 0 18.5153 -2.9-15 1.86699 18.5153 -1. 1.86699+128 ++128 18.9862 -.5 1.69894 +CORD2R 3339001 0 18.9631 -2.9-18 1.86699 18.9631 3.38-17 .866999+129 ++129 19.4415 .145628 1.36699 +CORD2R 3349001 0 18.9631 2.99-18 1.86699 18.9631 6.27-17 2.867+130 ++130 19.4415 -.14562 2.367 +CORD2R 5409001 0 8.01838 -5.9-18 .197264 8.01838 1.56-18 -.80273+131 ++131 8.51838 -4.8-16 -.30273 +CORD2R 6409001 0 8.01838 5.97-18 .197264 8.01838 1.35-17 1.19726+132 ++132 8.51838 4.92-16 .697264 +GRID 100001 2. 0.0 1.55 +GRID 100002 3.9431 0.0 1.55 +GRID 100003 5.8862 0.0 1.55 +GRID 100004 7.8293 0.0 1.55 +GRID 100005 9.7724 0.0 1.55 +GRID 100006 11.7155 0.0 1.55 +GRID 100007 13.6586 0.0 1.55 +GRID 100008 15.6017 0.0 1.55 +GRID 100009 17.5448 0.0 1.55 +GRID 100010 19.4879 0.0 1.55 +GRID 100011 21.431 0.0 1.55 +GRID 33290001 18.5153-2.95-15 1.86699 +GRID 33290002 18.6428-2.58-15 2.22425 +GRID 33290003 18.7703-2.22-15 2.5815 +GRID 33290004 18.8978-1.85-15 2.93875 +GRID 33290005 19.0253-1.48-15 3.296 +GRID 33290006 19.1528-1.11-15 3.65323 +GRID 33290007 19.2803-7.49-16 4.01048 +GRID 33290008 19.4078-3.82-16 4.36775 +GRID 33290009 19.5352-1.46-17 4.72498 +GRID 33290101 17.416 0.0 1.86699 +GRID 33290102 17.6219 0.0 2.22425 +GRID 33290103 17.8279 0.0 2.5815 +GRID 33290104 18.034 0.0 2.93875 +GRID 33290105 18.2399 0.0 3.296 +GRID 33290106 18.4459 0.0 3.65323 +GRID 33290107 18.6519 0.0 4.01048 +GRID 33290108 18.8579 0.0 4.36775 +GRID 33290109 19.0639 0.0 4.72498 +GRID 33290201 21.26 0.0 1.86699 +GRID 33290202 21.1914 0.0 2.22425 +GRID 33290203 21.1229 0.0 2.5815 +GRID 33290204 21.0545 0.0 2.93875 +GRID 33290205 20.986 0.0 3.296 +GRID 33290206 20.9175 0.0 3.65323 +GRID 33290207 20.849 0.0 4.01048 +GRID 33290208 20.7805 0.0 4.36775 +GRID 33290209 20.712 0.0 4.72498 +GRID 33390001 18.9631-2.99-18 1.86699 +GRID 33390002 19.136-.567768 1.86699 +GRID 33390003 19.3204-1.17339 1.86699 +GRID 33390004 19.5048-1.77901 1.86699 +GRID 33390005 19.6892-2.38462 1.86699 +GRID 33390006 19.8736-2.99025 1.86699 +GRID 33390007 20.0579-3.59587 1.86699 +GRID 33390008 20.2436-4.20568 1.86699 +GRID 33390101 18.02-5.94-18 1.86699 +GRID 33390102 18.2727-.567768 1.86699 +GRID 33390103 18.5422-1.17339 1.86699 +GRID 33390104 18.8118-1.77901 1.86699 +GRID 33390105 19.0814-2.38462 1.86699 +GRID 33390106 19.3509-2.99025 1.86699 +GRID 33390107 19.6205-3.59587 1.86699 +GRID 33390108 19.8919-4.20568 1.86699 +GRID 33390201 21.0049-5.94-18 1.86699 +GRID 33390202 21.0049-.567768 1.86699 +GRID 33390203 21.0049-1.17339 1.86699 +GRID 33390204 21.0049-1.77901 1.86699 +GRID 33390205 21.0049-2.38462 1.86699 +GRID 33390206 21.0049-2.99025 1.86699 +GRID 33390207 21.0049-3.59587 1.86699 +GRID 33390208 21.0049-4.20568 1.86699 +GRID 33490001 18.9631 2.99-18 1.86699 +GRID 33490002 19.136 .567768 1.86699 +GRID 33490003 19.3204 1.17339 1.86699 +GRID 33490004 19.5048 1.77901 1.86699 +GRID 33490005 19.6892 2.38462 1.86699 +GRID 33490006 19.8736 2.99025 1.86699 +GRID 33490007 20.0579 3.59587 1.86699 +GRID 33490008 20.2436 4.20568 1.86699 +GRID 33490101 18.02 5.94-18 1.86699 +GRID 33490102 18.2727 .567768 1.86699 +GRID 33490103 18.5422 1.17339 1.86699 +GRID 33490104 18.8118 1.77901 1.86699 +GRID 33490105 19.0814 2.38462 1.86699 +GRID 33490106 19.3509 2.99025 1.86699 +GRID 33490107 19.6205 3.59587 1.86699 +GRID 33490108 19.8919 4.20568 1.86699 +GRID 33490201 21.0049 5.94-18 1.86699 +GRID 33490202 21.0049 .567768 1.86699 +GRID 33490203 21.0049 1.17339 1.86699 +GRID 33490204 21.0049 1.77901 1.86699 +GRID 33490205 21.0049 2.38462 1.86699 +GRID 33490206 21.0049 2.99025 1.86699 +GRID 33490207 21.0049 3.59587 1.86699 +GRID 33490208 21.0049 4.20568 1.86699 +GRID 54090001 8.01838-5.97-18 .197264 +GRID 54090002 8.01838-.526238 .197264 +GRID 54090003 8.01838-1.05246 .197264 +GRID 54090004 8.01838 -1.5787 .197264 +GRID 54090005 8.01838-2.10126 .197264 +GRID 54090006 8.01838 -2.4987 .197264 +GRID 54090007 8.01838-3.15374 .197264 +GRID 54090008 8.01838 -3.68 .197264 +GRID 54090009 8.11153-4.11688 .229737 +GRID 54090010 8.20469-4.55376 .26221 +GRID 54090011 8.29785-4.99065 .294684 +GRID 54090012 8.391-5.42753 .327157 +GRID 54090013 8.48416-5.86441 .35963 +GRID 54090014 8.57731-6.30129 .392103 +GRID 54090015 8.67047-6.73817 .424576 +GRID 54090016 8.76362-7.17505 .457049 +GRID 54090017 8.85677-7.61191 .489521 +GRID 54090018 8.94992-8.04878 .521993 +GRID 54090019 9.04307-8.48564 .554465 +GRID 54090020 9.13622 -8.9225 .586936 +GRID 54090021 9.22937-9.35934 .619406 +GRID 54090022 9.32252-9.79619 .651877 +GRID 54090023 9.41565-10.2329 .684344 +GRID 54090024 9.5088-10.6698 .716813 +GRID 54090025 9.60192-11.1065 .749273 +GRID 54090026 9.69505-11.5433 .781741 +GRID 54090027 9.78819-11.9801 .814206 +GRID 54090028 9.88129-12.4167 .846662 +GRID 54090029 9.97439-12.8533 .879115 +GRID 54090030 10.0674-13.2898 .911555 +GRID 54090031 10.1607-13.7275 .94409 +GRID 54090101 6.88999-1.11-15 .150999 +GRID 54090102 6.88999-.526238 .150999 +GRID 54090103 6.88999-1.05246 .150999 +GRID 54090104 6.88999 -1.5787 .150999 +GRID 54090105 6.88999-2.10126 .150999 +GRID 54090106 6.88999 -2.4987 .150999 +GRID 54090107 6.88999-3.15374 .150999 +GRID 54090108 6.88999 -3.68 .150999 +GRID 54090109 7.01607 -4.1169 .184822 +GRID 54090110 7.14214-4.55381 .218644 +GRID 54090111 7.26822-4.99072 .252467 +GRID 54090112 7.39429-5.42763 .286289 +GRID 54090113 7.52036-5.86454 .320111 +GRID 54090114 7.64643-6.30144 .353933 +GRID 54090115 7.7725-6.73834 .387755 +GRID 54090116 7.89858-7.17525 .421577 +GRID 54090117 8.02464-7.61214 .455399 +GRID 54090118 8.15072-8.04904 .48922 +GRID 54090119 8.27678-8.48593 .523041 +GRID 54090120 8.40285-8.92283 .556862 +GRID 54090121 8.52892 -9.3597 .590682 +GRID 54090122 8.65498-9.79659 .624503 +GRID 54090123 8.78104-10.2334 .658321 +GRID 54090124 8.9071-10.6703 .69214 +GRID 54090125 9.03313 -11.107 .725952 +GRID 54090126 9.15919-11.5439 .759771 +GRID 54090127 9.28525-11.9808 .793589 +GRID 54090128 9.41128-12.4175 .827399 +GRID 54090129 9.5373-12.8542 .861207 +GRID 54090130 9.66328-13.2908 .895006 +GRID 54090131 9.78999-13.7299 .929 +GRID 54090201 11.21-1.11-15 .150999 +GRID 54090202 11.21-.526238 .150999 +GRID 54090203 11.21-1.05246 .150999 +GRID 54090204 11.21 -1.5787 .150999 +GRID 54090205 11.21-2.10126 .150999 +GRID 54090206 11.21 -2.4987 .150999 +GRID 54090207 11.21-3.15374 .150999 +GRID 54090208 11.21 -3.68 .150999 +GRID 54090209 11.21 -4.1169 .184822 +GRID 54090210 11.21-4.55381 .218644 +GRID 54090211 11.21-4.99072 .252467 +GRID 54090212 11.21-5.42763 .286289 +GRID 54090213 11.21-5.86454 .320111 +GRID 54090214 11.21-6.30144 .353933 +GRID 54090215 11.21-6.73834 .387755 +GRID 54090216 11.21-7.17525 .421577 +GRID 54090217 11.21-7.61214 .455399 +GRID 54090218 11.21-8.04904 .48922 +GRID 54090219 11.21-8.48593 .523041 +GRID 54090220 11.21-8.92283 .556862 +GRID 54090221 11.21 -9.3597 .590682 +GRID 54090222 11.21-9.79659 .624503 +GRID 54090223 11.21-10.2334 .658321 +GRID 54090224 11.21-10.6703 .69214 +GRID 54090225 11.21 -11.107 .725952 +GRID 54090226 11.21-11.5439 .759771 +GRID 54090227 11.21-11.9808 .793589 +GRID 54090228 11.21-12.4175 .827399 +GRID 54090229 11.21-12.8542 .861207 +GRID 54090230 11.21-13.2908 .895006 +GRID 54090231 11.21-13.7254 .92865 +GRID 54100001 5.129 -2.745 0.0 +GRID 54100002 5.897 -2.745 0.0 +GRID 54100003 7.105 -2.745 0.0 +GRID 64090001 8.01838 5.97-18 .197264 +GRID 64090002 8.01838 .526238 .197264 +GRID 64090003 8.01838 1.05246 .197264 +GRID 64090004 8.01838 1.5787 .197264 +GRID 64090005 8.01838 2.10126 .197264 +GRID 64090006 8.01838 2.4987 .197264 +GRID 64090007 8.01838 3.15374 .197264 +GRID 64090008 8.01838 3.68 .197264 +GRID 64090009 8.11154 4.11691 .229739 +GRID 64090010 8.20471 4.55383 .262215 +GRID 64090011 8.29787 4.99074 .294691 +GRID 64090012 8.39103 5.42766 .327167 +GRID 64090013 8.48419 5.86458 .359643 +GRID 64090014 8.57735 6.30149 .392118 +GRID 64090015 8.67052 6.73841 .424594 +GRID 64090016 8.76368 7.17533 .45707 +GRID 64090017 8.85684 7.61223 .489544 +GRID 64090018 8.95 8.04914 .52202 +GRID 64090019 9.04316 8.48605 .554495 +GRID 64090020 9.13632 8.92295 .586969 +GRID 64090021 9.22948 9.35985 .619444 +GRID 64090022 9.32264 9.79675 .651918 +GRID 64090023 9.41578 10.2335 .684389 +GRID 64090024 9.50895 10.6705 .716865 +GRID 64090025 9.60209 11.1073 .749333 +GRID 64090026 9.69525 11.5442 .781807 +GRID 64090027 9.78838 11.981 .814273 +GRID 64090028 9.88153 12.4178 .846744 +GRID 64090029 9.97465 12.8545 .879204 +GRID 64090030 10.0677 13.2913 .911667 +GRID 64090031 10.1607 13.7275 .94409 +GRID 64090101 6.88999 1.11-15 .150999 +GRID 64090102 6.88999 .526238 .150999 +GRID 64090103 6.88999 1.05246 .150999 +GRID 64090104 6.88999 1.5787 .150999 +GRID 64090105 6.88999 2.10126 .150999 +GRID 64090106 6.88999 2.4987 .150999 +GRID 64090107 6.88999 3.15374 .150999 +GRID 64090108 6.88999 3.68 .150999 +GRID 64090109 7.01608 4.11693 .184824 +GRID 64090110 7.14216 4.55388 .218649 +GRID 64090111 7.26824 4.99081 .252474 +GRID 64090112 7.39433 5.42776 .286299 +GRID 64090113 7.52041 5.86471 .320124 +GRID 64090114 7.64649 6.30164 .353949 +GRID 64090115 7.77257 6.73858 .387774 +GRID 64090116 7.89866 7.17553 .421599 +GRID 64090117 8.02474 7.61246 .455423 +GRID 64090118 8.15082 8.0494 .489248 +GRID 64090119 8.2769 8.48635 .523073 +GRID 64090120 8.40298 8.92328 .556897 +GRID 64090121 8.52906 9.36021 .590722 +GRID 64090122 8.65514 9.79715 .624547 +GRID 64090123 8.78121 10.234 .658367 +GRID 64090124 8.9073 10.671 .692194 +GRID 64090125 9.03336 11.1078 .726014 +GRID 64090126 9.15945 11.5448 .75984 +GRID 64090127 9.28551 11.9817 .793659 +GRID 64090128 9.41159 12.4186 .827484 +GRID 64090129 9.53765 12.8554 .8613 +GRID 64090130 9.66372 13.2923 .895122 +GRID 64090131 9.78999 13.7299 .929 +GRID 64090201 11.21 1.11-15 .150999 +GRID 64090202 11.21 .526238 .150999 +GRID 64090203 11.21 1.05246 .150999 +GRID 64090204 11.21 1.5787 .150999 +GRID 64090205 11.21 2.10126 .150999 +GRID 64090206 11.21 2.4987 .150999 +GRID 64090207 11.21 3.15374 .150999 +GRID 64090208 11.21 3.68 .150999 +GRID 64090209 11.21 4.11693 .184824 +GRID 64090210 11.21 4.55388 .218649 +GRID 64090211 11.21 4.99081 .252474 +GRID 64090212 11.21 5.42776 .286299 +GRID 64090213 11.21 5.86471 .320124 +GRID 64090214 11.21 6.30164 .353949 +GRID 64090215 11.21 6.73858 .387774 +GRID 64090216 11.21 7.17553 .421599 +GRID 64090217 11.21 7.61246 .455423 +GRID 64090218 11.21 8.0494 .489248 +GRID 64090219 11.21 8.48635 .523073 +GRID 64090220 11.21 8.92328 .556897 +GRID 64090221 11.21 9.36021 .590722 +GRID 64090222 11.21 9.79715 .624547 +GRID 64090223 11.21 10.234 .658367 +GRID 64090224 11.21 10.671 .692194 +GRID 64090225 11.21 11.1078 .726014 +GRID 64090226 11.21 11.5448 .75984 +GRID 64090227 11.21 11.9817 .793659 +GRID 64090228 11.21 12.4186 .827484 +GRID 64090229 11.21 12.8554 .8613 +GRID 64090230 11.21 13.2923 .895122 +GRID 64090231 11.21 13.7254 .92865 +GRID 64100001 5.129 2.745 0.0 +GRID 64100002 5.897 2.745 0.0 +GRID 64100003 7.105 2.745 0.0 +MAT1 332001 7.+10 2.69+10 .3 0.0 +MAT1 333001 7.+10 2.69+10 .3 0.0 +MAT1 334001 7.+10 2.69+10 .3 0.0 +MAT1 540001 7.+10 2.69+10 .3 0.0 +MAT1 640001 7.+10 2.69+10 .3 0.0 +PBAR 3328001 332001 .02567 .0006 .03703 .0376 0.0 +PBAR 3328002 332001 .02201 .000371 .02726 .02769 0.0 +PBAR 3328003 332001 .01864 2.662-4 .01956 .01987 0.0 +PBAR 3328004 332001 .01554 1.855-4 .01363 .01384 0.0 +PBAR 3328005 332001 .01273 1.247-4 .009158 .009303 0.0 +PBAR 3328006 332001 .0102 8.02-5 .005894 .005987 0.0 +PBAR 3328007 332001 .007948 .0001 .00359 .00365 0.0 +PBAR 3328008 332001 .005979 2.787-5 .002045 .002078 0.0 +PBAR 3338001 333001 .01214 .0002 .00942 .00959 0.0 +PBAR 3338002 333001 .01001 9.633-5 .00643 .00654 0.0 +PBAR 3338003 333001 .008032 6.214-5 .004149 .00422 0.0 +PBAR 3338004 333001 .006268 3.796-5 .002537 .002581 0.0 +PBAR 3338005 333001 .004724 2.164-5 .001449 .001474 0.0 +PBAR 3338006 333001 .003399 1.132-5 .00075 .000769 0.0 +PBAR 3338007 333001 .00229 5.236-6 3.431-4 3.547-4 0.0 +PBAR 3348001 334001 .01214 .0002 .00942 .00959 0.0 +PBAR 3348002 334001 .01001 9.633-5 .00643 .00654 0.0 +PBAR 3348003 334001 .008032 6.214-5 .004149 .00422 0.0 +PBAR 3348004 334001 .006268 3.796-5 .002537 .002581 0.0 +PBAR 3348005 334001 .004724 2.164-5 .001449 .001474 0.0 +PBAR 3348006 334001 .003399 1.132-5 .00075 .000769 0.0 +PBAR 3348007 334001 .00229 5.236-6 3.431-4 3.547-4 0.0 +PBAR 5408001 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408002 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408003 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408004 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408005 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408006 540001 .03553 4.376-4 .002553 .002663 0.0 +PBAR 5408007 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408008 540001 .03446 4.039-4 .002403 .002504 0.0 +PBAR 5408009 540001 .03237 .000341 .002116 .002201 0.0 +PBAR 5408010 540001 .03034 2.866-4 .001857 .001928 0.0 +PBAR 5408011 540001 .02838 2.396-4 .001622 .001682 0.0 +PBAR 5408012 540001 .0265 1.994-4 .001412 .001462 0.0 +PBAR 5408013 540001 .02468 1.649-4 .001223 .001264 0.0 +PBAR 5408014 540001 .02293 1.356-4 .001054 .001088 0.0 +PBAR 5408015 540001 .02125 1.108-4 9.038-4 9.315-4 0.0 +PBAR 5408016 540001 .01964 8.989-5 7.706-4 7.931-4 0.0 +PBAR 5408017 540001 .0181 7.242-5 6.531-4 6.712-4 0.0 +PBAR 5408018 540001 .01662 5.788-5 5.499-4 5.644-4 0.0 +PBAR 5408019 540001 .01521 4.589-5 4.597-4 4.712-4 0.0 +PBAR 5408020 540001 .01386 3.605-5 3.813-4 3.904-4 0.0 +PBAR 5408021 540001 .01258 2.805-5 3.137-4 3.207-4 0.0 +PBAR 5408022 540001 .01137 2.159-5 2.556-4 .000261 0.0 +PBAR 5408023 540001 .01022 1.642-5 2.062-4 2.103-4 0.0 +PBAR 5408024 540001 .009136 1.234-5 1.644-4 1.675-4 0.0 +PBAR 5408025 540001 .008114 9.14-6 1.295-4 1.318-4 0.0 +PBAR 5408026 540001 .007156 6.66-6 1.005-4 1.022-4 0.0 +PBAR 5408027 540001 .00626 4.78-6 7.681-5 7.801-5 0.0 +PBAR 5408028 540001 .005426 3.36-6 5.762-5 5.846-5 0.0 +PBAR 5408029 540001 .004654 2.3-6 4.233-5 4.291-5 0.0 +PBAR 5408030 540001 .003943 1.6-6 3.035-5 3.074-5 0.0 +PBAR 6408001 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408002 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408003 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408004 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408005 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408006 640001 .03553 4.376-4 .002553 .002663 0.0 +PBAR 6408007 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408008 640001 .03446 4.039-4 .002403 .002504 0.0 +PBAR 6408009 640001 .03237 .000341 .002116 .002201 0.0 +PBAR 6408010 640001 .03034 2.866-4 .001857 .001928 0.0 +PBAR 6408011 640001 .02838 2.396-4 .001622 .001682 0.0 +PBAR 6408012 640001 .0265 1.994-4 .001412 .001462 0.0 +PBAR 6408013 640001 .02468 1.649-4 .001223 .001264 0.0 +PBAR 6408014 640001 .02293 1.356-4 .001054 .001088 0.0 +PBAR 6408015 640001 .02125 1.108-4 9.038-4 9.315-4 0.0 +PBAR 6408016 640001 .01964 8.989-5 7.706-4 7.931-4 0.0 +PBAR 6408017 640001 .0181 7.242-5 6.531-4 6.712-4 0.0 +PBAR 6408018 640001 .01662 5.788-5 5.499-4 5.644-4 0.0 +PBAR 6408019 640001 .01521 4.589-5 4.597-4 4.712-4 0.0 +PBAR 6408020 640001 .01386 3.605-5 3.813-4 3.904-4 0.0 +PBAR 6408021 640001 .01258 2.805-5 3.137-4 3.207-4 0.0 +PBAR 6408022 640001 .01137 2.159-5 2.556-4 .000261 0.0 +PBAR 6408023 640001 .01022 1.642-5 2.062-4 2.103-4 0.0 +PBAR 6408024 640001 .009136 1.234-5 1.644-4 1.675-4 0.0 +PBAR 6408025 640001 .008114 9.14-6 1.295-4 1.318-4 0.0 +PBAR 6408026 640001 .007156 6.66-6 1.005-4 1.022-4 0.0 +PBAR 6408027 640001 .00626 4.78-6 7.681-5 7.801-5 0.0 +PBAR 6408028 640001 .005426 3.36-6 5.762-5 5.846-5 0.0 +PBAR 6408029 640001 .004654 2.3-6 4.233-5 4.291-5 0.0 +PBAR 6408030 640001 .003943 1.6-6 3.035-5 3.074-5 0.0 +CRBE2 100000 100004 123456 100001 100002 100003 100005 100006+133 ++133 100007 100008 100009 100010 100011 +CRBE2 200001 100004 1234566409000154090001 +CRBE2 200002 100010 1234563349000133390001 +CRBE2 200003 100010 12345633290001 +CRBE2 3329010133290001 1234563329010133290201 +CRBE2 3329010233290002 1234563329010233290202 +CRBE2 3329010333290003 1234563329010333290203 +CRBE2 3329010433290004 1234563329010433290204 +CRBE2 3329010533290005 1234563329010533290205 +CRBE2 3329010633290006 1234563329010633290206 +CRBE2 3329010733290007 1234563329010733290207 +CRBE2 3329010833290008 1234563329010833290208 +CRBE2 3329010933290009 1234563329010933290209 +CRBE2 3339010133390001 1234563339010133390201 +CRBE2 3339010233390002 1234563339010233390202 +CRBE2 3339010333390003 1234563339010333390203 +CRBE2 3339010433390004 1234563339010433390204 +CRBE2 3339010533390005 1234563339010533390205 +CRBE2 3339010633390006 1234563339010633390206 +CRBE2 3339010733390007 1234563339010733390207 +CRBE2 3339010833390008 1234563339010833390208 +CRBE2 3349010133490001 1234563349010133490201 +CRBE2 3349010233490002 1234563349010233490202 +CRBE2 3349010333490003 1234563349010333490203 +CRBE2 3349010433490004 1234563349010433490204 +CRBE2 3349010533490005 1234563349010533490205 +CRBE2 3349010633490006 1234563349010633490206 +CRBE2 3349010733490007 1234563349010733490207 +CRBE2 3349010833490008 1234563349010833490208 +CRBE2 5409010154090001 1234565409010154090201 +CRBE2 5409010254090002 1234565409010254090202 +CRBE2 5409010354090003 1234565409010354090203 +CRBE2 5409010454090004 1234565409010454090204 +CRBE2 5409010554090005 1234565409010554090205 +CRBE2 5409010654090006 1234565409010654090206 +CRBE2 5409010754090007 1234565409010754090207 +CRBE2 5409010854090008 1234565409010854090208 +CRBE2 5409010954090009 1234565409010954090209 +CRBE2 5409011054090010 1234565409011054090210 +CRBE2 5409011154090011 1234565409011154090211 +CRBE2 5409011254090012 1234565409011254090212 +CRBE2 5409011354090013 1234565409011354090213 +CRBE2 5409011454090014 1234565409011454090214 +CRBE2 5409011554090015 1234565409011554090215 +CRBE2 5409011654090016 1234565409011654090216 +CRBE2 5409011754090017 1234565409011754090217 +CRBE2 5409011854090018 1234565409011854090218 +CRBE2 5409011954090019 1234565409011954090219 +CRBE2 5409012054090020 1234565409012054090220 +CRBE2 5409012154090021 1234565409012154090221 +CRBE2 5409012254090022 1234565409012254090222 +CRBE2 5409012354090023 1234565409012354090223 +CRBE2 5409012454090024 1234565409012454090224 +CRBE2 5409012554090025 1234565409012554090225 +CRBE2 5409012654090026 1234565409012654090226 +CRBE2 5409012754090027 1234565409012754090227 +CRBE2 5409012854090028 1234565409012854090228 +CRBE2 5409012954090029 1234565409012954090229 +CRBE2 5409013054090030 1234565409013054090230 +CRBE2 5409013154090031 1234565409013154090231 +CRBE2 5410000054090006 123456541000015410000254100003 +CRBE2 6409010164090001 1234566409010164090201 +CRBE2 6409010264090002 1234566409010264090202 +CRBE2 6409010364090003 1234566409010364090203 +CRBE2 6409010464090004 1234566409010464090204 +CRBE2 6409010564090005 1234566409010564090205 +CRBE2 6409010664090006 1234566409010664090206 +CRBE2 6409010764090007 1234566409010764090207 +CRBE2 6409010864090008 1234566409010864090208 +CRBE2 6409010964090009 1234566409010964090209 +CRBE2 6409011064090010 1234566409011064090210 +CRBE2 6409011164090011 1234566409011164090211 +CRBE2 6409011264090012 1234566409011264090212 +CRBE2 6409011364090013 1234566409011364090213 +CRBE2 6409011464090014 1234566409011464090214 +CRBE2 6409011564090015 1234566409011564090215 +CRBE2 6409011664090016 1234566409011664090216 +CRBE2 6409011764090017 1234566409011764090217 +CRBE2 6409011864090018 1234566409011864090218 +CRBE2 6409011964090019 1234566409011964090219 +CRBE2 6409012064090020 1234566409012064090220 +CRBE2 6409012164090021 1234566409012164090221 +CRBE2 6409012264090022 1234566409012264090222 +CRBE2 6409012364090023 1234566409012364090223 +CRBE2 6409012464090024 1234566409012464090224 +CRBE2 6409012564090025 1234566409012564090225 +CRBE2 6409012664090026 1234566409012664090226 +CRBE2 6409012764090027 1234566409012764090227 +CRBE2 6409012864090028 1234566409012864090228 +CRBE2 6409012964090029 1234566409012964090229 +CRBE2 6409013064090030 1234566409013064090230 +CRBE2 6409013164090031 1234566409013164090231 +CRBE2 6410000064090006 123456641000016410000264100003 +$ +ENDDATA diff --git a/doc/tutorials/DC3_model/fem/Nastran95/Nast95_structure_only.bdf b/doc/tutorials/DC3_model/fem/Nastran95/Nast95_structure_only.bdf new file mode 100644 index 00000000..2d14b8f8 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/Nastran95/Nast95_structure_only.bdf @@ -0,0 +1,779 @@ +ID DC3,Aircraft +SOL 3 +DIAG 14,18 +ALTER 52 +OUTPUT2 KGG,GM,USET,,//0/11////*MSC* $ +ENDALTER +CEND +$ +METHOD=100 +ECHO=NONE +$ +BEGIN BULK +PARAM GRDPNT 0 +$------><------><------><------><------><------><------><------><------> +EIGR 100 MGIV 0.1 15.0 +$------><------><------><------><------><------><------><------><------> +$ +CBAR 3328001 33280013329000133290002 0.0 1. 0.0 +CBAR 3328002 33280023329000233290003 0.0 1. 0.0 +CBAR 3328003 33280033329000333290004 0.0 1. 0.0 +CBAR 3328004 33280043329000433290005 0.0 1. 0.0 +CBAR 3328005 33280053329000533290006 0.0 1. 0.0 +CBAR 3328006 33280063329000633290007 0.0 1. 0.0 +CBAR 3328007 33280073329000733290008 0.0 1. 0.0 +CBAR 3328008 33280083329000833290009 0.0 1. 0.0 +CBAR 3338001 33380013339000133390002 0.0 0.0 1. +CBAR 3338002 33380023339000233390003 0.0 0.0 1. +CBAR 3338003 33380033339000333390004 0.0 0.0 1. +CBAR 3338004 33380043339000433390005 0.0 0.0 1. +CBAR 3338005 33380053339000533390006 0.0 0.0 1. +CBAR 3338006 33380063339000633390007 0.0 0.0 1. +CBAR 3338007 33380073339000733390008 0.0 0.0 1. +CBAR 3348001 33480013349000133490002 0.0 0.0 1. +CBAR 3348002 33480023349000233490003 0.0 0.0 1. +CBAR 3348003 33480033349000333490004 0.0 0.0 1. +CBAR 3348004 33480043349000433490005 0.0 0.0 1. +CBAR 3348005 33480053349000533490006 0.0 0.0 1. +CBAR 3348006 33480063349000633490007 0.0 0.0 1. +CBAR 3348007 33480073349000733490008 0.0 0.0 1. +CBAR 5408001 54080015409000154090002 0.0 0.0 1. +CBAR 5408002 54080025409000254090003 0.0 0.0 1. +CBAR 5408003 54080035409000354090004 0.0 0.0 1. +CBAR 5408004 54080045409000454090005 0.0 0.0 1. +CBAR 5408005 54080055409000554090006 0.0 0.0 1. +CBAR 5408006 54080065409000654090007 0.0 0.0 1. +CBAR 5408007 54080075409000754090008 0.0 0.0 1. +CBAR 5408008 54080085409000854090009 0.0 0.0 1. +CBAR 5408009 54080095409000954090010 0.0 0.0 1. +CBAR 5408010 54080105409001054090011 0.0 0.0 1. +CBAR 5408011 54080115409001154090012 0.0 0.0 1. +CBAR 5408012 54080125409001254090013 0.0 0.0 1. +CBAR 5408013 54080135409001354090014 0.0 0.0 1. +CBAR 5408014 54080145409001454090015 0.0 0.0 1. +CBAR 5408015 54080155409001554090016 0.0 0.0 1. +CBAR 5408016 54080165409001654090017 0.0 0.0 1. +CBAR 5408017 54080175409001754090018 0.0 0.0 1. +CBAR 5408018 54080185409001854090019 0.0 0.0 1. +CBAR 5408019 54080195409001954090020 0.0 0.0 1. +CBAR 5408020 54080205409002054090021 0.0 0.0 1. +CBAR 5408021 54080215409002154090022 0.0 0.0 1. +CBAR 5408022 54080225409002254090023 0.0 0.0 1. +CBAR 5408023 54080235409002354090024 0.0 0.0 1. +CBAR 5408024 54080245409002454090025 0.0 0.0 1. +CBAR 5408025 54080255409002554090026 0.0 0.0 1. +CBAR 5408026 54080265409002654090027 0.0 0.0 1. +CBAR 5408027 54080275409002754090028 0.0 0.0 1. +CBAR 5408028 54080285409002854090029 0.0 0.0 1. +CBAR 5408029 54080295409002954090030 0.0 0.0 1. +CBAR 5408030 54080305409003054090031 0.0 0.0 1. +CBAR 6408001 64080016409000164090002 0.0 0.0 1. +CBAR 6408002 64080026409000264090003 0.0 0.0 1. +CBAR 6408003 64080036409000364090004 0.0 0.0 1. +CBAR 6408004 64080046409000464090005 0.0 0.0 1. +CBAR 6408005 64080056409000564090006 0.0 0.0 1. +CBAR 6408006 64080066409000664090007 0.0 0.0 1. +CBAR 6408007 64080076409000764090008 0.0 0.0 1. +CBAR 6408008 64080086409000864090009 0.0 0.0 1. +CBAR 6408009 64080096409000964090010 0.0 0.0 1. +CBAR 6408010 64080106409001064090011 0.0 0.0 1. +CBAR 6408011 64080116409001164090012 0.0 0.0 1. +CBAR 6408012 64080126409001264090013 0.0 0.0 1. +CBAR 6408013 64080136409001364090014 0.0 0.0 1. +CBAR 6408014 64080146409001464090015 0.0 0.0 1. +CBAR 6408015 64080156409001564090016 0.0 0.0 1. +CBAR 6408016 64080166409001664090017 0.0 0.0 1. +CBAR 6408017 64080176409001764090018 0.0 0.0 1. +CBAR 6408018 64080186409001864090019 0.0 0.0 1. +CBAR 6408019 64080196409001964090020 0.0 0.0 1. +CBAR 6408020 64080206409002064090021 0.0 0.0 1. +CBAR 6408021 64080216409002164090022 0.0 0.0 1. +CBAR 6408022 64080226409002264090023 0.0 0.0 1. +CBAR 6408023 64080236409002364090024 0.0 0.0 1. +CBAR 6408024 64080246409002464090025 0.0 0.0 1. +CBAR 6408025 64080256409002564090026 0.0 0.0 1. +CBAR 6408026 64080266409002664090027 0.0 0.0 1. +CBAR 6408027 64080276409002764090028 0.0 0.0 1. +CBAR 6408028 64080286409002864090029 0.0 0.0 1. +CBAR 6408029 64080296409002964090030 0.0 0.0 1. +CBAR 6408030 64080306409003064090031 0.0 0.0 1. +CONM2 110001 100001 0 19.313 0.0 0.0 0.0 +1 ++1 .358 0.0 0.0 0.0 0.0 0.0 +CONM2 110002 100002 0 141.008 0.0 0.0 0.0 +2 ++2 139.474 0.0 0.0 0.0 0.0 0.0 +CONM2 110003 100003 0 172.413 0.0 0.0 0.0 +3 ++3 254.959 0.0 0.0 0.0 0.0 0.0 +CONM2 110004 100004 0 184.19 0.0 0.0 0.0 +4 ++4 310.855 0.0 0.0 0.0 0.0 0.0 +CONM2 110005 100005 0 180.264 0.0 0.0 0.0 +5 ++5 291.4 0.0 0.0 0.0 0.0 0.0 +CONM2 110006 100006 0 178.301 0.0 0.0 0.0 +6 ++6 281.984 0.0 0.0 0.0 0.0 0.0 +CONM2 110007 100007 0 162.599 0.0 0.0 0.0 +7 ++7 213.852 0.0 0.0 0.0 0.0 0.0 +CONM2 110008 100008 0 146.896 0.0 0.0 0.0 +8 ++8 157.686 0.0 0.0 0.0 0.0 0.0 +CONM2 110009 100009 0 117.454 0.0 0.0 0.0 +9 ++9 80.606 0.0 0.0 0.0 0.0 0.0 +CONM2 110010 100010 0 74.272 0.0 0.0 0.0 +10 ++10 20.382 0.0 0.0 0.0 0.0 0.0 +CONM2 110011 100011 0 31.09 0.0 0.0 0.0 +11 ++11 1.495 0.0 0.0 0.0 0.0 0.0 +CONM2 332810133290001 0 13.375 0.0 0.0 0.0 +12 ++12 0.0 0.0 0.0 0.0 0.0 20.8867 +CONM2 332810233290002 0 23.065 0.0 0.0 0.0 +13 ++13 0.0 0.0 0.0 0.0 0.0 31.0587 +CONM2 332810333290003 0 19.654 0.0 0.0 0.0 +14 ++14 0.0 0.0 0.0 0.0 0.0 22.5508 +CONM2 332810433290004 0 16.515 0.0 0.0 0.0 +15 ++15 0.0 0.0 0.0 0.0 0.0 15.9235 +CONM2 332810533290005 0 13.65 0.0 0.0 0.0 +16 ++16 0.0 0.0 0.0 0.0 0.0 10.8769 +CONM2 332810633290006 0 11.057 0.0 0.0 0.0 +17 ++17 0.0 0.0 0.0 0.0 0.0 7.1371 +CONM2 332810733290007 0 8.737 0.0 0.0 0.0 +18 ++18 0.0 0.0 0.0 0.0 0.0 4.4562 +CONM2 332810833290008 0 6.69 0.0 0.0 0.0 +19 ++19 0.0 0.0 0.0 0.0 0.0 2.6126 +CONM2 332810933290009 0 2.458 0.0 0.0 0.0 +20 ++20 0.0 0.0 0.0 0.0 0.0 .7053 +CONM2 333810133390001 0 10.663 0.0 0.0 0.0 +21 ++21 0.0 0.0 8.6823 0.0 0.0 0.0 +CONM2 333810233390002 0 17.841 0.0 0.0 0.0 +22 ++22 0.0 0.0 12.5584 0.0 0.0 0.0 +CONM2 333810333390003 0 14.466 0.0 0.0 0.0 +23 ++23 0.0 0.0 8.5231 0.0 0.0 0.0 +CONM2 333810433390004 0 11.445 0.0 0.0 0.0 +24 ++24 0.0 0.0 5.3348 0.0 0.0 0.0 +CONM2 333810533390005 0 8.777 0.0 0.0 0.0 +25 ++25 0.0 0.0 3.1377 0.0 0.0 0.0 +CONM2 333810633390006 0 6.463 0.0 0.0 0.0 +26 ++26 0.0 0.0 1.7012 0.0 0.0 0.0 +CONM2 333810733390007 0 4.502 0.0 0.0 0.0 +27 ++27 0.0 0.0 .8284 0.0 0.0 0.0 +CONM2 333810833390008 0 1.442 0.0 0.0 0.0 +28 ++28 0.0 0.0 .1707 0.0 0.0 0.0 +CONM2 334810133490001 0 10.663 0.0 0.0 0.0 +29 ++29 0.0 0.0 8.6823 0.0 0.0 0.0 +CONM2 334810233490002 0 17.841 0.0 0.0 0.0 +30 ++30 0.0 0.0 12.5584 0.0 0.0 0.0 +CONM2 334810333490003 0 14.466 0.0 0.0 0.0 +31 ++31 0.0 0.0 8.5231 0.0 0.0 0.0 +CONM2 334810433490004 0 11.445 0.0 0.0 0.0 +32 ++32 0.0 0.0 5.3348 0.0 0.0 0.0 +CONM2 334810533490005 0 8.777 0.0 0.0 0.0 +33 ++33 0.0 0.0 3.1377 0.0 0.0 0.0 +CONM2 334810633490006 0 6.463 0.0 0.0 0.0 +34 ++34 0.0 0.0 1.7012 0.0 0.0 0.0 +CONM2 334810733490007 0 4.502 0.0 0.0 0.0 +35 ++35 0.0 0.0 .8284 0.0 0.0 0.0 +CONM2 334810833490008 0 1.442 0.0 0.0 0.0 +36 ++36 0.0 0.0 .1707 0.0 0.0 0.0 +CONM2 541000154090001 0 59.72 2.49282 5.97-18-.022564 +37 ++37 0.0 0.0 37.9722 0.0 0.0 0.0 +CONM2 541000254090002 0 56.44 2.49282 3.8-5-.022564 +38 ++38 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 541000354090003 0 55.44 2.49282-4.000-5-.022564 +39 ++39 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 541000454090004 0 55.44 2.49282 0.0-.022564 +40 ++40 0.0 0.0 75.6792 0.0 0.0 0.0 +CONM2 541000554090005 0 55.44 2.49282-4.000-5-.022564 +41 ++41 0.0 0.0 66.3853 0.0 0.0 0.0 +CONM2 541000654090006 0 55.44 2.49282 0.0-.022564 +42 ++42 0.0 0.0 75.9448 0.0 0.0 0.0 +CONM2 541000754090007 0 50.44 2.49282 4.-5-.022564 +43 ++43 0.0 0.0 85.2402 0.0 0.0 0.0 +CONM2 541000854090008 0 50.44 2.49282 0.0-.022564 +44 ++44 0.0 0.0 69.5011 0.0 0.0 0.0 +CONM2 541000954090009 0 46.711 2.21047-2.000-5-.021837 +45 ++45 0.0 0.0 55.533 0.0 0.0 0.0 +CONM2 541001054090010 0 40.074 2.34741-1.400-4 -.02121 +46 ++46 0.0 0.0 48.7313 0.0 0.0 0.0 +CONM2 541001154090011 0 37.527 2.27475-1.500-4-.020584 +47 ++47 0.0 0.0 42.5973 0.0 0.0 0.0 +CONM2 541001254090012 0 35.07 2.202 -1.7-4-.019957 +48 ++48 0.0 0.0 37.0827 0.0 0.0 0.0 +CONM2 541001354090013 0 22.702 2.12934 -1.9-4 -.01933 +49 ++49 0.0 0.0 32.1402 0.0 0.0 0.0 +CONM2 541001454090014 0 21.422 2.05659 -3.1-4-.018603 +50 ++50 0.0 0.0 27.7267 0.0 0.0 0.0 +CONM2 541001554090015 0 19.231 1.98383 -3.3-4-.017976 +51 ++51 0.0 0.0 23.7991 0.0 0.0 0.0 +CONM2 541001654090016 0 18.126 1.91118-3.500-4-.017349 +52 ++52 0.0 0.0 20.3172 0.0 0.0 0.0 +CONM2 541001754090017 0 16.108 1.83843-3.900-4-.016721 +53 ++53 0.0 0.0 17.2447 0.0 0.0 0.0 +CONM2 541001854090018 0 15.177 1.76578 -5.2-4-.016093 +54 ++54 0.0 0.0 14.5451 0.0 0.0 0.0 +CONM2 541001954090019 0 13.33 1.69303-5.600-4-.015365 +55 ++55 0.0 0.0 12.1843 0.0 0.0 0.0 +CONM2 541002054090020 0 12.569 1.62038-6.000-4-.014736 +56 ++56 0.0 0.0 10.1313 0.0 0.0 0.0 +CONM2 541002154090021 0 10.891 1.54763 -6.6-4-.014106 +57 ++57 0.0 0.0 8.3566 0.0 0.0 0.0 +CONM2 541002254090022 0 10.297 1.47488-8.100-4-.013477 +58 ++58 0.0 0.0 6.8312 0.0 0.0 0.0 +CONM2 541002354090023 0 8.786 1.40225 -9.-4-.012844 +59 ++59 0.0 0.0 5.5302 0.0 0.0 0.0 +CONM2 541002454090024 0 8.357 1.3295 -9.-4-.012113 +60 ++60 0.0 0.0 4.4292 0.0 0.0 0.0 +CONM2 541002554090025 0 7.01 1.25678 -1.1-3-.011473 +61 ++61 0.0 0.0 3.5048 0.0 0.0 0.0 +CONM2 541002654090026 0 6.744 1.18415 -1.2-3-.010841 +62 ++62 0.0 0.0 2.7359 0.0 0.0 0.0 +CONM2 541002754090027 0 5.558 1.11141 -1.2-3-.010206 +63 ++63 0.0 0.0 2.1038 0.0 0.0 0.0 +CONM2 541002854090028 0 4.453 1.03871-1.500-3-9.562-3 +64 ++64 0.0 0.0 1.5901 0.0 0.0 0.0 +CONM2 541002954090029 0 4.426 .96601 -1.7-3-8.915-3 +65 ++65 0.0 0.0 1.1782 0.0 0.0 0.0 +CONM2 541003054090030 0 3.478 .8934-2.000-3-8.155-3 +66 ++66 0.0 0.0 .8524 0.0 0.0 0.0 +CONM2 541003154090031 0 1.304 .8204-1.000-4 -7.69-3 +67 ++67 0.0 0.0 .3002 0.0 0.0 0.0 +CONM2 541100154100001 0 287.8 0.0 0.0 0.0 +68 ++68 73.257 0.0 0.0 0.0 0.0 0.0 +CONM2 541100254100002 0 470.95 0.0 0.0 0.0 +69 ++69 102.107 0.0 0.0 0.0 0.0 0.0 +CONM2 541100354100003 0 183.15 0.0 0.0 0.0 +70 ++70 33.408 0.0 0.0 0.0 0.0 0.0 +CONM2 641000164090001 0 59.72 2.49282-5.97-18-.022564 +71 ++71 0.0 0.0 37.9722 0.0 0.0 0.0 +CONM2 641000264090002 0 56.44 2.49282 -3.8-5-.022564 +72 ++72 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 641000364090003 0 55.44 2.492824.0000-5-.022564 +73 ++73 0.0 0.0 75.9433 0.0 0.0 0.0 +CONM2 641000464090004 0 55.44 2.49282 0.0-.022564 +74 ++74 0.0 0.0 75.6792 0.0 0.0 0.0 +CONM2 641000564090005 0 55.44 2.492824.0000-5-.022564 +75 ++75 0.0 0.0 66.3853 0.0 0.0 0.0 +CONM2 641000664090006 0 55.44 2.49282 0.0-.022564 +76 ++76 0.0 0.0 75.9448 0.0 0.0 0.0 +CONM2 641000764090007 0 50.44 2.49282 -4.-5-.022564 +77 ++77 0.0 0.0 85.2402 0.0 0.0 0.0 +CONM2 641000864090008 0 50.44 2.49282 0.0-.022564 +78 ++78 0.0 0.0 69.5011 0.0 0.0 0.0 +CONM2 641000964090009 0 46.711 2.21046 -1.-5-.021839 +79 ++79 0.0 0.0 55.533 0.0 0.0 0.0 +CONM2 641001064090010 0 40.074 2.347397.0000-5-.021215 +80 ++80 0.0 0.0 48.7313 0.0 0.0 0.0 +CONM2 641001164090011 0 37.527 2.274736.0000-5-.020591 +81 ++81 0.0 0.0 42.5973 0.0 0.0 0.0 +CONM2 641001264090012 0 35.07 2.20197 4.-5-.019967 +82 ++82 0.0 0.0 37.0827 0.0 0.0 0.0 +CONM2 641001364090013 0 22.702 2.129312.0000-5-.019343 +83 ++83 0.0 0.0 32.1402 0.0 0.0 0.0 +CONM2 641001464090014 0 21.422 2.05655 1.1-4-.018618 +84 ++84 0.0 0.0 27.7267 0.0 0.0 0.0 +CONM2 641001564090015 0 19.231 1.983789.0000-5-.017994 +85 ++85 0.0 0.0 23.7991 0.0 0.0 0.0 +CONM2 641001664090016 0 18.126 1.911127.0000-5 -.01737 +86 ++86 0.0 0.0 20.3172 0.0 0.0 0.0 +CONM2 641001764090017 0 16.108 1.838367.0000-5-.016744 +87 ++87 0.0 0.0 17.2447 0.0 0.0 0.0 +CONM2 641001864090018 0 15.177 1.76571.6000-4 -.01612 +88 ++88 0.0 0.0 14.5451 0.0 0.0 0.0 +CONM2 641001964090019 0 13.33 1.69294 1.5-4-.015395 +89 ++89 0.0 0.0 12.1843 0.0 0.0 0.0 +CONM2 641002064090020 0 12.569 1.62028 1.5-4-.014769 +90 ++90 0.0 0.0 10.1313 0.0 0.0 0.0 +CONM2 641002164090021 0 10.891 1.54752 1.5-4-.014144 +91 ++91 0.0 0.0 8.3566 0.0 0.0 0.0 +CONM2 641002264090022 0 10.297 1.474762.5000-4-.013518 +92 ++92 0.0 0.0 6.8312 0.0 0.0 0.0 +CONM2 641002364090023 0 8.786 1.402123.0000-4-.012889 +93 ++93 0.0 0.0 5.5302 0.0 0.0 0.0 +CONM2 641002464090024 0 8.357 1.32935 2.-4-.012165 +94 ++94 0.0 0.0 4.4292 0.0 0.0 0.0 +CONM2 641002564090025 0 7.01 1.25661 3.-4-.011533 +95 ++95 0.0 0.0 3.5048 0.0 0.0 0.0 +CONM2 641002764090027 0 5.558 1.11122 3.-4-.010273 +96 ++96 0.0 0.0 2.1038 0.0 0.0 0.0 +CONM2 641002864090028 0 4.453 1.038474.0000-4-9.644-3 +97 ++97 0.0 0.0 1.5901 0.0 0.0 0.0 +CONM2 641002964090029 0 4.426 .965755.0000-4-9.004-3 +98 ++98 0.0 0.0 1.1782 0.0 0.0 0.0 +CONM2 641003064090030 0 3.478 .89315.0000-4-8.267-3 +99 ++99 0.0 0.0 .8524 0.0 0.0 0.0 +CONM2 641003164090031 0 1.304 .82041.0000-4 -7.69-3 +100 ++100 0.0 0.0 .3002 0.0 0.0 0.0 +CONM2 641100164100001 0 287.8 0.0 0.0 0.0 +101 ++101 73.257 0.0 0.0 0.0 0.0 0.0 +CONM2 641100264100002 0 470.95 0.0 0.0 0.0 +102 ++102 102.107 0.0 0.0 0.0 0.0 0.0 +CONM2 641100364100003 0 183.15 0.0 0.0 0.0 +103 ++103 33.408 0.0 0.0 0.0 0.0 0.0 +CONM2 410012664090026 0 6.744 1.18395 3.-4-.010907 +104 ++104 0.0 0.0 2.7359 0.0 0.0 0.0 +CORD2R 3329001 0 18.5153 -2.9-15 1.86699 18.5153 -1. 1.86699+105 ++105 18.9862 -.5 1.69894 +CORD2R 3339001 0 18.9631 -2.9-18 1.86699 18.9631 3.38-17 .866999+106 ++106 19.4415 .145628 1.36699 +CORD2R 3349001 0 18.9631 2.99-18 1.86699 18.9631 6.27-17 2.867+107 ++107 19.4415 -.14562 2.367 +CORD2R 5409001 0 8.01838 -5.9-18 .197264 8.01838 1.56-18 -.80273+108 ++108 8.51838 -4.8-16 -.30273 +CORD2R 6409001 0 8.01838 5.97-18 .197264 8.01838 1.35-17 1.19726+109 ++109 8.51838 4.92-16 .697264 +GRID 100001 2. 0.0 1.55 +GRID 100002 3.9431 0.0 1.55 +GRID 100003 5.8862 0.0 1.55 +GRID 100004 7.8293 0.0 1.55 +GRID 100005 9.7724 0.0 1.55 +GRID 100006 11.7155 0.0 1.55 +GRID 100007 13.6586 0.0 1.55 +GRID 100008 15.6017 0.0 1.55 +GRID 100009 17.5448 0.0 1.55 +GRID 100010 19.4879 0.0 1.55 +GRID 100011 21.431 0.0 1.55 +GRID 33290001 18.5153-2.95-15 1.86699 +GRID 33290002 18.6428-2.58-15 2.22425 +GRID 33290003 18.7703-2.22-15 2.5815 +GRID 33290004 18.8978-1.85-15 2.93875 +GRID 33290005 19.0253-1.48-15 3.296 +GRID 33290006 19.1528-1.11-15 3.65323 +GRID 33290007 19.2803-7.49-16 4.01048 +GRID 33290008 19.4078-3.82-16 4.36775 +GRID 33290009 19.5352-1.46-17 4.72498 +GRID 33290101 17.416 0.0 1.86699 +GRID 33290102 17.6219 0.0 2.22425 +GRID 33290103 17.8279 0.0 2.5815 +GRID 33290104 18.034 0.0 2.93875 +GRID 33290105 18.2399 0.0 3.296 +GRID 33290106 18.4459 0.0 3.65323 +GRID 33290107 18.6519 0.0 4.01048 +GRID 33290108 18.8579 0.0 4.36775 +GRID 33290109 19.0639 0.0 4.72498 +GRID 33290201 21.26 0.0 1.86699 +GRID 33290202 21.1914 0.0 2.22425 +GRID 33290203 21.1229 0.0 2.5815 +GRID 33290204 21.0545 0.0 2.93875 +GRID 33290205 20.986 0.0 3.296 +GRID 33290206 20.9175 0.0 3.65323 +GRID 33290207 20.849 0.0 4.01048 +GRID 33290208 20.7805 0.0 4.36775 +GRID 33290209 20.712 0.0 4.72498 +GRID 33390001 18.9631-2.99-18 1.86699 +GRID 33390002 19.136-.567768 1.86699 +GRID 33390003 19.3204-1.17339 1.86699 +GRID 33390004 19.5048-1.77901 1.86699 +GRID 33390005 19.6892-2.38462 1.86699 +GRID 33390006 19.8736-2.99025 1.86699 +GRID 33390007 20.0579-3.59587 1.86699 +GRID 33390008 20.2436-4.20568 1.86699 +GRID 33390101 18.02-5.94-18 1.86699 +GRID 33390102 18.2727-.567768 1.86699 +GRID 33390103 18.5422-1.17339 1.86699 +GRID 33390104 18.8118-1.77901 1.86699 +GRID 33390105 19.0814-2.38462 1.86699 +GRID 33390106 19.3509-2.99025 1.86699 +GRID 33390107 19.6205-3.59587 1.86699 +GRID 33390108 19.8919-4.20568 1.86699 +GRID 33390201 21.0049-5.94-18 1.86699 +GRID 33390202 21.0049-.567768 1.86699 +GRID 33390203 21.0049-1.17339 1.86699 +GRID 33390204 21.0049-1.77901 1.86699 +GRID 33390205 21.0049-2.38462 1.86699 +GRID 33390206 21.0049-2.99025 1.86699 +GRID 33390207 21.0049-3.59587 1.86699 +GRID 33390208 21.0049-4.20568 1.86699 +GRID 33490001 18.9631 2.99-18 1.86699 +GRID 33490002 19.136 .567768 1.86699 +GRID 33490003 19.3204 1.17339 1.86699 +GRID 33490004 19.5048 1.77901 1.86699 +GRID 33490005 19.6892 2.38462 1.86699 +GRID 33490006 19.8736 2.99025 1.86699 +GRID 33490007 20.0579 3.59587 1.86699 +GRID 33490008 20.2436 4.20568 1.86699 +GRID 33490101 18.02 5.94-18 1.86699 +GRID 33490102 18.2727 .567768 1.86699 +GRID 33490103 18.5422 1.17339 1.86699 +GRID 33490104 18.8118 1.77901 1.86699 +GRID 33490105 19.0814 2.38462 1.86699 +GRID 33490106 19.3509 2.99025 1.86699 +GRID 33490107 19.6205 3.59587 1.86699 +GRID 33490108 19.8919 4.20568 1.86699 +GRID 33490201 21.0049 5.94-18 1.86699 +GRID 33490202 21.0049 .567768 1.86699 +GRID 33490203 21.0049 1.17339 1.86699 +GRID 33490204 21.0049 1.77901 1.86699 +GRID 33490205 21.0049 2.38462 1.86699 +GRID 33490206 21.0049 2.99025 1.86699 +GRID 33490207 21.0049 3.59587 1.86699 +GRID 33490208 21.0049 4.20568 1.86699 +GRID 54090001 8.01838-5.97-18 .197264 +GRID 54090002 8.01838-.526238 .197264 +GRID 54090003 8.01838-1.05246 .197264 +GRID 54090004 8.01838 -1.5787 .197264 +GRID 54090005 8.01838-2.10126 .197264 +GRID 54090006 8.01838 -2.4987 .197264 +GRID 54090007 8.01838-3.15374 .197264 +GRID 54090008 8.01838 -3.68 .197264 +GRID 54090009 8.11153-4.11688 .229737 +GRID 54090010 8.20469-4.55376 .26221 +GRID 54090011 8.29785-4.99065 .294684 +GRID 54090012 8.391-5.42753 .327157 +GRID 54090013 8.48416-5.86441 .35963 +GRID 54090014 8.57731-6.30129 .392103 +GRID 54090015 8.67047-6.73817 .424576 +GRID 54090016 8.76362-7.17505 .457049 +GRID 54090017 8.85677-7.61191 .489521 +GRID 54090018 8.94992-8.04878 .521993 +GRID 54090019 9.04307-8.48564 .554465 +GRID 54090020 9.13622 -8.9225 .586936 +GRID 54090021 9.22937-9.35934 .619406 +GRID 54090022 9.32252-9.79619 .651877 +GRID 54090023 9.41565-10.2329 .684344 +GRID 54090024 9.5088-10.6698 .716813 +GRID 54090025 9.60192-11.1065 .749273 +GRID 54090026 9.69505-11.5433 .781741 +GRID 54090027 9.78819-11.9801 .814206 +GRID 54090028 9.88129-12.4167 .846662 +GRID 54090029 9.97439-12.8533 .879115 +GRID 54090030 10.0674-13.2898 .911555 +GRID 54090031 10.1607-13.7275 .94409 +GRID 54090101 6.88999-1.11-15 .150999 +GRID 54090102 6.88999-.526238 .150999 +GRID 54090103 6.88999-1.05246 .150999 +GRID 54090104 6.88999 -1.5787 .150999 +GRID 54090105 6.88999-2.10126 .150999 +GRID 54090106 6.88999 -2.4987 .150999 +GRID 54090107 6.88999-3.15374 .150999 +GRID 54090108 6.88999 -3.68 .150999 +GRID 54090109 7.01607 -4.1169 .184822 +GRID 54090110 7.14214-4.55381 .218644 +GRID 54090111 7.26822-4.99072 .252467 +GRID 54090112 7.39429-5.42763 .286289 +GRID 54090113 7.52036-5.86454 .320111 +GRID 54090114 7.64643-6.30144 .353933 +GRID 54090115 7.7725-6.73834 .387755 +GRID 54090116 7.89858-7.17525 .421577 +GRID 54090117 8.02464-7.61214 .455399 +GRID 54090118 8.15072-8.04904 .48922 +GRID 54090119 8.27678-8.48593 .523041 +GRID 54090120 8.40285-8.92283 .556862 +GRID 54090121 8.52892 -9.3597 .590682 +GRID 54090122 8.65498-9.79659 .624503 +GRID 54090123 8.78104-10.2334 .658321 +GRID 54090124 8.9071-10.6703 .69214 +GRID 54090125 9.03313 -11.107 .725952 +GRID 54090126 9.15919-11.5439 .759771 +GRID 54090127 9.28525-11.9808 .793589 +GRID 54090128 9.41128-12.4175 .827399 +GRID 54090129 9.5373-12.8542 .861207 +GRID 54090130 9.66328-13.2908 .895006 +GRID 54090131 9.78999-13.7299 .929 +GRID 54090201 11.21-1.11-15 .150999 +GRID 54090202 11.21-.526238 .150999 +GRID 54090203 11.21-1.05246 .150999 +GRID 54090204 11.21 -1.5787 .150999 +GRID 54090205 11.21-2.10126 .150999 +GRID 54090206 11.21 -2.4987 .150999 +GRID 54090207 11.21-3.15374 .150999 +GRID 54090208 11.21 -3.68 .150999 +GRID 54090209 11.21 -4.1169 .184822 +GRID 54090210 11.21-4.55381 .218644 +GRID 54090211 11.21-4.99072 .252467 +GRID 54090212 11.21-5.42763 .286289 +GRID 54090213 11.21-5.86454 .320111 +GRID 54090214 11.21-6.30144 .353933 +GRID 54090215 11.21-6.73834 .387755 +GRID 54090216 11.21-7.17525 .421577 +GRID 54090217 11.21-7.61214 .455399 +GRID 54090218 11.21-8.04904 .48922 +GRID 54090219 11.21-8.48593 .523041 +GRID 54090220 11.21-8.92283 .556862 +GRID 54090221 11.21 -9.3597 .590682 +GRID 54090222 11.21-9.79659 .624503 +GRID 54090223 11.21-10.2334 .658321 +GRID 54090224 11.21-10.6703 .69214 +GRID 54090225 11.21 -11.107 .725952 +GRID 54090226 11.21-11.5439 .759771 +GRID 54090227 11.21-11.9808 .793589 +GRID 54090228 11.21-12.4175 .827399 +GRID 54090229 11.21-12.8542 .861207 +GRID 54090230 11.21-13.2908 .895006 +GRID 54090231 11.21-13.7254 .92865 +GRID 54100001 5.129 -2.745 0.0 +GRID 54100002 5.897 -2.745 0.0 +GRID 54100003 7.105 -2.745 0.0 +GRID 64090001 8.01838 5.97-18 .197264 +GRID 64090002 8.01838 .526238 .197264 +GRID 64090003 8.01838 1.05246 .197264 +GRID 64090004 8.01838 1.5787 .197264 +GRID 64090005 8.01838 2.10126 .197264 +GRID 64090006 8.01838 2.4987 .197264 +GRID 64090007 8.01838 3.15374 .197264 +GRID 64090008 8.01838 3.68 .197264 +GRID 64090009 8.11154 4.11691 .229739 +GRID 64090010 8.20471 4.55383 .262215 +GRID 64090011 8.29787 4.99074 .294691 +GRID 64090012 8.39103 5.42766 .327167 +GRID 64090013 8.48419 5.86458 .359643 +GRID 64090014 8.57735 6.30149 .392118 +GRID 64090015 8.67052 6.73841 .424594 +GRID 64090016 8.76368 7.17533 .45707 +GRID 64090017 8.85684 7.61223 .489544 +GRID 64090018 8.95 8.04914 .52202 +GRID 64090019 9.04316 8.48605 .554495 +GRID 64090020 9.13632 8.92295 .586969 +GRID 64090021 9.22948 9.35985 .619444 +GRID 64090022 9.32264 9.79675 .651918 +GRID 64090023 9.41578 10.2335 .684389 +GRID 64090024 9.50895 10.6705 .716865 +GRID 64090025 9.60209 11.1073 .749333 +GRID 64090026 9.69525 11.5442 .781807 +GRID 64090027 9.78838 11.981 .814273 +GRID 64090028 9.88153 12.4178 .846744 +GRID 64090029 9.97465 12.8545 .879204 +GRID 64090030 10.0677 13.2913 .911667 +GRID 64090031 10.1607 13.7275 .94409 +GRID 64090101 6.88999 1.11-15 .150999 +GRID 64090102 6.88999 .526238 .150999 +GRID 64090103 6.88999 1.05246 .150999 +GRID 64090104 6.88999 1.5787 .150999 +GRID 64090105 6.88999 2.10126 .150999 +GRID 64090106 6.88999 2.4987 .150999 +GRID 64090107 6.88999 3.15374 .150999 +GRID 64090108 6.88999 3.68 .150999 +GRID 64090109 7.01608 4.11693 .184824 +GRID 64090110 7.14216 4.55388 .218649 +GRID 64090111 7.26824 4.99081 .252474 +GRID 64090112 7.39433 5.42776 .286299 +GRID 64090113 7.52041 5.86471 .320124 +GRID 64090114 7.64649 6.30164 .353949 +GRID 64090115 7.77257 6.73858 .387774 +GRID 64090116 7.89866 7.17553 .421599 +GRID 64090117 8.02474 7.61246 .455423 +GRID 64090118 8.15082 8.0494 .489248 +GRID 64090119 8.2769 8.48635 .523073 +GRID 64090120 8.40298 8.92328 .556897 +GRID 64090121 8.52906 9.36021 .590722 +GRID 64090122 8.65514 9.79715 .624547 +GRID 64090123 8.78121 10.234 .658367 +GRID 64090124 8.9073 10.671 .692194 +GRID 64090125 9.03336 11.1078 .726014 +GRID 64090126 9.15945 11.5448 .75984 +GRID 64090127 9.28551 11.9817 .793659 +GRID 64090128 9.41159 12.4186 .827484 +GRID 64090129 9.53765 12.8554 .8613 +GRID 64090130 9.66372 13.2923 .895122 +GRID 64090131 9.78999 13.7299 .929 +GRID 64090201 11.21 1.11-15 .150999 +GRID 64090202 11.21 .526238 .150999 +GRID 64090203 11.21 1.05246 .150999 +GRID 64090204 11.21 1.5787 .150999 +GRID 64090205 11.21 2.10126 .150999 +GRID 64090206 11.21 2.4987 .150999 +GRID 64090207 11.21 3.15374 .150999 +GRID 64090208 11.21 3.68 .150999 +GRID 64090209 11.21 4.11693 .184824 +GRID 64090210 11.21 4.55388 .218649 +GRID 64090211 11.21 4.99081 .252474 +GRID 64090212 11.21 5.42776 .286299 +GRID 64090213 11.21 5.86471 .320124 +GRID 64090214 11.21 6.30164 .353949 +GRID 64090215 11.21 6.73858 .387774 +GRID 64090216 11.21 7.17553 .421599 +GRID 64090217 11.21 7.61246 .455423 +GRID 64090218 11.21 8.0494 .489248 +GRID 64090219 11.21 8.48635 .523073 +GRID 64090220 11.21 8.92328 .556897 +GRID 64090221 11.21 9.36021 .590722 +GRID 64090222 11.21 9.79715 .624547 +GRID 64090223 11.21 10.234 .658367 +GRID 64090224 11.21 10.671 .692194 +GRID 64090225 11.21 11.1078 .726014 +GRID 64090226 11.21 11.5448 .75984 +GRID 64090227 11.21 11.9817 .793659 +GRID 64090228 11.21 12.4186 .827484 +GRID 64090229 11.21 12.8554 .8613 +GRID 64090230 11.21 13.2923 .895122 +GRID 64090231 11.21 13.7254 .92865 +GRID 64100001 5.129 2.745 0.0 +GRID 64100002 5.897 2.745 0.0 +GRID 64100003 7.105 2.745 0.0 +MAT1 332001 7.+10 2.69+10 .3 0.0 +MAT1 333001 7.+10 2.69+10 .3 0.0 +MAT1 334001 7.+10 2.69+10 .3 0.0 +MAT1 540001 7.+10 2.69+10 .3 0.0 +MAT1 640001 7.+10 2.69+10 .3 0.0 +PBAR 3328001 332001 .02567 .0006 .03703 .0376 0.0 +PBAR 3328002 332001 .02201 .000371 .02726 .02769 0.0 +PBAR 3328003 332001 .01864 2.662-4 .01956 .01987 0.0 +PBAR 3328004 332001 .01554 1.855-4 .01363 .01384 0.0 +PBAR 3328005 332001 .01273 1.247-4 .009158 .009303 0.0 +PBAR 3328006 332001 .0102 8.02-5 .005894 .005987 0.0 +PBAR 3328007 332001 .007948 .0001 .00359 .00365 0.0 +PBAR 3328008 332001 .005979 2.787-5 .002045 .002078 0.0 +PBAR 3338001 333001 .01214 .0002 .00942 .00959 0.0 +PBAR 3338002 333001 .01001 9.633-5 .00643 .00654 0.0 +PBAR 3338003 333001 .008032 6.214-5 .004149 .00422 0.0 +PBAR 3338004 333001 .006268 3.796-5 .002537 .002581 0.0 +PBAR 3338005 333001 .004724 2.164-5 .001449 .001474 0.0 +PBAR 3338006 333001 .003399 1.132-5 .00075 .000769 0.0 +PBAR 3338007 333001 .00229 5.236-6 3.431-4 3.547-4 0.0 +PBAR 3348001 334001 .01214 .0002 .00942 .00959 0.0 +PBAR 3348002 334001 .01001 9.633-5 .00643 .00654 0.0 +PBAR 3348003 334001 .008032 6.214-5 .004149 .00422 0.0 +PBAR 3348004 334001 .006268 3.796-5 .002537 .002581 0.0 +PBAR 3348005 334001 .004724 2.164-5 .001449 .001474 0.0 +PBAR 3348006 334001 .003399 1.132-5 .00075 .000769 0.0 +PBAR 3348007 334001 .00229 5.236-6 3.431-4 3.547-4 0.0 +PBAR 5408001 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408002 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408003 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408004 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408005 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408006 540001 .03553 4.376-4 .002553 .002663 0.0 +PBAR 5408007 540001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 5408008 540001 .03446 4.039-4 .002403 .002504 0.0 +PBAR 5408009 540001 .03237 .000341 .002116 .002201 0.0 +PBAR 5408010 540001 .03034 2.866-4 .001857 .001928 0.0 +PBAR 5408011 540001 .02838 2.396-4 .001622 .001682 0.0 +PBAR 5408012 540001 .0265 1.994-4 .001412 .001462 0.0 +PBAR 5408013 540001 .02468 1.649-4 .001223 .001264 0.0 +PBAR 5408014 540001 .02293 1.356-4 .001054 .001088 0.0 +PBAR 5408015 540001 .02125 1.108-4 9.038-4 9.315-4 0.0 +PBAR 5408016 540001 .01964 8.989-5 7.706-4 7.931-4 0.0 +PBAR 5408017 540001 .0181 7.242-5 6.531-4 6.712-4 0.0 +PBAR 5408018 540001 .01662 5.788-5 5.499-4 5.644-4 0.0 +PBAR 5408019 540001 .01521 4.589-5 4.597-4 4.712-4 0.0 +PBAR 5408020 540001 .01386 3.605-5 3.813-4 3.904-4 0.0 +PBAR 5408021 540001 .01258 2.805-5 3.137-4 3.207-4 0.0 +PBAR 5408022 540001 .01137 2.159-5 2.556-4 .000261 0.0 +PBAR 5408023 540001 .01022 1.642-5 2.062-4 2.103-4 0.0 +PBAR 5408024 540001 .009136 1.234-5 1.644-4 1.675-4 0.0 +PBAR 5408025 540001 .008114 9.14-6 1.295-4 1.318-4 0.0 +PBAR 5408026 540001 .007156 6.66-6 1.005-4 1.022-4 0.0 +PBAR 5408027 540001 .00626 4.78-6 7.681-5 7.801-5 0.0 +PBAR 5408028 540001 .005426 3.36-6 5.762-5 5.846-5 0.0 +PBAR 5408029 540001 .004654 2.3-6 4.233-5 4.291-5 0.0 +PBAR 5408030 540001 .003943 1.6-6 3.035-5 3.074-5 0.0 +PBAR 6408001 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408002 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408003 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408004 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408005 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408006 640001 .03553 4.376-4 .002553 .002663 0.0 +PBAR 6408007 640001 .03553 4.375-4 .002553 .002663 0.0 +PBAR 6408008 640001 .03446 4.039-4 .002403 .002504 0.0 +PBAR 6408009 640001 .03237 .000341 .002116 .002201 0.0 +PBAR 6408010 640001 .03034 2.866-4 .001857 .001928 0.0 +PBAR 6408011 640001 .02838 2.396-4 .001622 .001682 0.0 +PBAR 6408012 640001 .0265 1.994-4 .001412 .001462 0.0 +PBAR 6408013 640001 .02468 1.649-4 .001223 .001264 0.0 +PBAR 6408014 640001 .02293 1.356-4 .001054 .001088 0.0 +PBAR 6408015 640001 .02125 1.108-4 9.038-4 9.315-4 0.0 +PBAR 6408016 640001 .01964 8.989-5 7.706-4 7.931-4 0.0 +PBAR 6408017 640001 .0181 7.242-5 6.531-4 6.712-4 0.0 +PBAR 6408018 640001 .01662 5.788-5 5.499-4 5.644-4 0.0 +PBAR 6408019 640001 .01521 4.589-5 4.597-4 4.712-4 0.0 +PBAR 6408020 640001 .01386 3.605-5 3.813-4 3.904-4 0.0 +PBAR 6408021 640001 .01258 2.805-5 3.137-4 3.207-4 0.0 +PBAR 6408022 640001 .01137 2.159-5 2.556-4 .000261 0.0 +PBAR 6408023 640001 .01022 1.642-5 2.062-4 2.103-4 0.0 +PBAR 6408024 640001 .009136 1.234-5 1.644-4 1.675-4 0.0 +PBAR 6408025 640001 .008114 9.14-6 1.295-4 1.318-4 0.0 +PBAR 6408026 640001 .007156 6.66-6 1.005-4 1.022-4 0.0 +PBAR 6408027 640001 .00626 4.78-6 7.681-5 7.801-5 0.0 +PBAR 6408028 640001 .005426 3.36-6 5.762-5 5.846-5 0.0 +PBAR 6408029 640001 .004654 2.3-6 4.233-5 4.291-5 0.0 +PBAR 6408030 640001 .003943 1.6-6 3.035-5 3.074-5 0.0 +CRBE2 100000 100004 123456 100001 100002 100003 100005 100006+110 ++110 100007 100008 100009 100010 100011 +CRBE2 200001 100004 1234566409000154090001 +CRBE2 200002 100010 1234563349000133390001 +CRBE2 200003 100010 12345633290001 +CRBE2 3329010133290001 1234563329010133290201 +CRBE2 3329010233290002 1234563329010233290202 +CRBE2 3329010333290003 1234563329010333290203 +CRBE2 3329010433290004 1234563329010433290204 +CRBE2 3329010533290005 1234563329010533290205 +CRBE2 3329010633290006 1234563329010633290206 +CRBE2 3329010733290007 1234563329010733290207 +CRBE2 3329010833290008 1234563329010833290208 +CRBE2 3329010933290009 1234563329010933290209 +CRBE2 3339010133390001 1234563339010133390201 +CRBE2 3339010233390002 1234563339010233390202 +CRBE2 3339010333390003 1234563339010333390203 +CRBE2 3339010433390004 1234563339010433390204 +CRBE2 3339010533390005 1234563339010533390205 +CRBE2 3339010633390006 1234563339010633390206 +CRBE2 3339010733390007 1234563339010733390207 +CRBE2 3339010833390008 1234563339010833390208 +CRBE2 3349010133490001 1234563349010133490201 +CRBE2 3349010233490002 1234563349010233490202 +CRBE2 3349010333490003 1234563349010333490203 +CRBE2 3349010433490004 1234563349010433490204 +CRBE2 3349010533490005 1234563349010533490205 +CRBE2 3349010633490006 1234563349010633490206 +CRBE2 3349010733490007 1234563349010733490207 +CRBE2 3349010833490008 1234563349010833490208 +CRBE2 5409010154090001 1234565409010154090201 +CRBE2 5409010254090002 1234565409010254090202 +CRBE2 5409010354090003 1234565409010354090203 +CRBE2 5409010454090004 1234565409010454090204 +CRBE2 5409010554090005 1234565409010554090205 +CRBE2 5409010654090006 1234565409010654090206 +CRBE2 5409010754090007 1234565409010754090207 +CRBE2 5409010854090008 1234565409010854090208 +CRBE2 5409010954090009 1234565409010954090209 +CRBE2 5409011054090010 1234565409011054090210 +CRBE2 5409011154090011 1234565409011154090211 +CRBE2 5409011254090012 1234565409011254090212 +CRBE2 5409011354090013 1234565409011354090213 +CRBE2 5409011454090014 1234565409011454090214 +CRBE2 5409011554090015 1234565409011554090215 +CRBE2 5409011654090016 1234565409011654090216 +CRBE2 5409011754090017 1234565409011754090217 +CRBE2 5409011854090018 1234565409011854090218 +CRBE2 5409011954090019 1234565409011954090219 +CRBE2 5409012054090020 1234565409012054090220 +CRBE2 5409012154090021 1234565409012154090221 +CRBE2 5409012254090022 1234565409012254090222 +CRBE2 5409012354090023 1234565409012354090223 +CRBE2 5409012454090024 1234565409012454090224 +CRBE2 5409012554090025 1234565409012554090225 +CRBE2 5409012654090026 1234565409012654090226 +CRBE2 5409012754090027 1234565409012754090227 +CRBE2 5409012854090028 1234565409012854090228 +CRBE2 5409012954090029 1234565409012954090229 +CRBE2 5409013054090030 1234565409013054090230 +CRBE2 5409013154090031 1234565409013154090231 +CRBE2 5410000054090006 123456541000015410000254100003 +CRBE2 6409010164090001 1234566409010164090201 +CRBE2 6409010264090002 1234566409010264090202 +CRBE2 6409010364090003 1234566409010364090203 +CRBE2 6409010464090004 1234566409010464090204 +CRBE2 6409010564090005 1234566409010564090205 +CRBE2 6409010664090006 1234566409010664090206 +CRBE2 6409010764090007 1234566409010764090207 +CRBE2 6409010864090008 1234566409010864090208 +CRBE2 6409010964090009 1234566409010964090209 +CRBE2 6409011064090010 1234566409011064090210 +CRBE2 6409011164090011 1234566409011164090211 +CRBE2 6409011264090012 1234566409011264090212 +CRBE2 6409011364090013 1234566409011364090213 +CRBE2 6409011464090014 1234566409011464090214 +CRBE2 6409011564090015 1234566409011564090215 +CRBE2 6409011664090016 1234566409011664090216 +CRBE2 6409011764090017 1234566409011764090217 +CRBE2 6409011864090018 1234566409011864090218 +CRBE2 6409011964090019 1234566409011964090219 +CRBE2 6409012064090020 1234566409012064090220 +CRBE2 6409012164090021 1234566409012164090221 +CRBE2 6409012264090022 1234566409012264090222 +CRBE2 6409012364090023 1234566409012364090223 +CRBE2 6409012464090024 1234566409012464090224 +CRBE2 6409012564090025 1234566409012564090225 +CRBE2 6409012664090026 1234566409012664090226 +CRBE2 6409012764090027 1234566409012764090227 +CRBE2 6409012864090028 1234566409012864090228 +CRBE2 6409012964090029 1234566409012964090229 +CRBE2 6409013064090030 1234566409013064090230 +CRBE2 6409013164090031 1234566409013164090231 +CRBE2 6410000064090006 123456641000016410000264100003 +$ +ENDDATA +$ diff --git a/doc/tutorials/DC3_model/fem/NastranSOL103/SOL103_M3.bdf b/doc/tutorials/DC3_model/fem/NastranSOL103/SOL103_M3.bdf new file mode 100644 index 00000000..446087a2 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/NastranSOL103/SOL103_M3.bdf @@ -0,0 +1,44 @@ +NASTRAN OP2NEW=0 +$ Direct Text Input for File Management Section +ASSIGN OUTPUT2 = 'uset.op2', UNIT = 12 +$ Direct Text Input for Executive Control +MALTER 'AFTER UPSTREAM SUPERELEMENT MATRIX AND LOAD ASSEMBLY'$ +CRDB_MTX MGG//'MGG' $ +CRDB_MTX KGG//'KGG' $ +ENDALTER $ +MALTER 'MALTER:AFTER SUPERELEMENT MATRIX AND LOAD REDUCTION TO A-SET' $ +CRDB_MTX GM//'GM' $ +OUTPUT2 USET//0/12///'USET' $ +ENDALTER $ +ECHOON $ +$ +SOL 103 +CEND +$ +METHOD=100 +ECHO=NONE +$ +BEGIN BULK +PARAM GRDPNT 0 +$------><------><------><------><------><------><------><------><------> +EIGRL 100 100 +$------><------><------><------><------><------><------><------><------> +HDF5OUT PRCISION 64 CMPRMTHD NONE MTX YES +$ +$----------------------------------------------------------------------- +$ Structural model +include '../assembly/structure_only.bdf' +$ +$ Non-structural masses +include '../nonstructural_masses/systems-mass.csv' +include '../nonstructural_masses/crew_and_equipment-mass.csv' +$ +$ Low fuel +include '../nonstructural_masses/fueltanks_low_mi.SCONM2' +include '../nonstructural_masses/fueltanks_low_mi.SCONM2_mrd' +$ +$ Payload mass +include '../nonstructural_masses/payload.csv' +$ +ENDDATA +$ diff --git a/doc/tutorials/DC3_model/fem/NastranSOL103/SOL103_structure_only.bdf b/doc/tutorials/DC3_model/fem/NastranSOL103/SOL103_structure_only.bdf new file mode 100644 index 00000000..4d669bb5 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/NastranSOL103/SOL103_structure_only.bdf @@ -0,0 +1,33 @@ +NASTRAN OP2NEW=0 +$ Direct Text Input for File Management Section +ASSIGN OUTPUT2 = 'uset.op2', UNIT = 12 +$ Direct Text Input for Executive Control +MALTER 'AFTER UPSTREAM SUPERELEMENT MATRIX AND LOAD ASSEMBLY'$ +CRDB_MTX MGG//'MGG' $ +CRDB_MTX KGG//'KGG' $ +ENDALTER $ +MALTER 'MALTER:AFTER SUPERELEMENT MATRIX AND LOAD REDUCTION TO A-SET' $ +CRDB_MTX GM//'GM' $ +OUTPUT2 USET//0/12///'USET' $ +ENDALTER $ +ECHOON $ +$ +SOL 103 +CEND +$ +METHOD=100 +ECHO=NONE +$ +BEGIN BULK +PARAM GRDPNT 0 +$------><------><------><------><------><------><------><------><------> +EIGRL 100 100 +$------><------><------><------><------><------><------><------><------> +HDF5OUT PRCISION 64 CMPRMTHD NONE MTX YES +$ +$----------------------------------------------------------------------- +$ Structural model +include '../assembly/structure_only.bdf' +$ +ENDDATA +$ diff --git a/doc/tutorials/DC3_model/fem/assembly/structure_only.bdf b/doc/tutorials/DC3_model/fem/assembly/structure_only.bdf new file mode 100644 index 00000000..cdc5bac5 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/assembly/structure_only.bdf @@ -0,0 +1,72 @@ +$ +$----------------------------------------------------------------------- +$ Interface FUS/W-S, FUS/W-P +$------><------><------><------><------><------><------><------><------> +RBE2 200001 100004 1234566409000154090001 +$ +$ Interface FUS/HTP-S, FUS/HTP-P +$------><------><------><------><------><------><------><------><------> +RBE2 200002 100010 1234563349000133390001 +$ +$ Interface FUS/VTP +$------><------><------><------><------><------><------><------><------> +RBE2 200003 100010 12345633290001 +$ +$ +$----------------------------------------------------------------------- +$ Fuselage +include '../export_FUS.csv' +$----------------------------------------------------------------------- +$ Left-wing +$ LRA +include '../left-wing/left-wing.GRID_LREFAX_5400001' +include '../left-wing/left-wing.RBE2_LREFAX_5400001' +include '../left-wing/left-wing.CORD2R_LREFAX' +$ bdf model +include '../left-wing/left-wing.MAT_ZR' +include '../left-wing/export_left-wing.csv' +$----------------------------------------------------------------------- +$ Right-wing +$ LRA +include '../right-wing/right-wing.GRID_LREFAX_6400001' +include '../right-wing/right-wing.RBE2_LREFAX_6400001' +include '../right-wing/right-wing.CORD2R_LREFAX' +$ bdf model +include '../right-wing/right-wing.MAT_ZR' +include '../right-wing/export_right-wing.csv' +$----------------------------------------------------------------------- +$ Left-ht +$ LRA +include '../left-ht/left-ht.GRID_LREFAX_3330001' +include '../left-ht/left-ht.RBE2_LREFAX_3330001' +include '../left-ht/left-ht.CORD2R_LREFAX' +$ bdf model +include '../left-ht/left-ht.MAT_ZR' +include '../left-ht/export_left-ht.csv' +$----------------------------------------------------------------------- +$ Right-ht +$ LRA +include '../right-ht/right-ht.GRID_LREFAX_3340001' +include '../right-ht/right-ht.RBE2_LREFAX_3340001' +include '../right-ht/right-ht.CORD2R_LREFAX' +$ bdf model +include '../right-ht/right-ht.MAT_ZR' +include '../right-ht/export_right-ht.csv' +$----------------------------------------------------------------------- +$ Vt +$ LRA +include '../vt/vt.GRID_LREFAX_3320001' +include '../vt/vt.RBE2_LREFAX_3320001' +include '../vt/vt.CORD2R_LREFAX' +$ bdf model +include '../vt/vt.MAT_ZR' +include '../vt/export_vt.csv' +$----------------------------------------------------------------------- +$ Left-nacell +include '../export_left-nacell.csv' +$----------------------------------------------------------------------- +$ Right-nacell +include '../export_right-nacell.csv' +$----------------------------------------------------------------------- + + diff --git a/doc/tutorials/DC3_model/fem/nonstructural_masses/crew_and_equipment-mass.csv b/doc/tutorials/DC3_model/fem/nonstructural_masses/crew_and_equipment-mass.csv new file mode 100644 index 00000000..219879f0 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/nonstructural_masses/crew_and_equipment-mass.csv @@ -0,0 +1,15 @@ +$ Mono, 10pt, column width 17mm, no spacing +$2345678 + +$crew eqnodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 111001 7.700 0.000 0.000 + + + + +$crew eqmass model +$crew eq EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 121001 100004 -1 425.470 7.700 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 diff --git a/doc/tutorials/DC3_model/fem/nonstructural_masses/fueltanks_low_mi.SCONM2 b/doc/tutorials/DC3_model/fem/nonstructural_masses/fueltanks_low_mi.SCONM2 new file mode 100644 index 00000000..0fe6934f --- /dev/null +++ b/doc/tutorials/DC3_model/fem/nonstructural_masses/fueltanks_low_mi.SCONM2 @@ -0,0 +1,46 @@ +$ +$*********************************************************************** +$ Created by ModGen (Version 2.2401.01 (latest version)) +$ Operating System: Linux +$ Date & Time: 2024-02-21 15:41:39 +$*********************************************************************** +$ +$ - The file contains CONM2-cards as result of the mass integration +$ task of modgen +$ - SCONM2 mass entities are attached to MGRIDs +$ - The total mass and c.g. is written below the CONM2 cards +$ +$----------------------------------------------------------------------- +$ +CONM2 9000164090001 0 161.473.0145699.2631190-.156335 + ++ 14.7777 5.67-14 92.9288 -.30998 -4.3-16 100.509 +CONM2 9000264090003 0 161.466.0145699 -.26309-.156336 + ++ 14.7761 -3.98-6 92.9245 -.30996 -4.37-5 100.502 +CONM2 9000364090003 0 161.473.0145699.2631100-.156335 + ++ 14.7777 -7.97-6 92.9291 -.30998 -8.74-5 100.509 +CONM2 9000464090004 0 160.344.0145699.2612600-.156335 + ++ 14.5198 -7.91-6 92.2792 -.30781 -8.67-5 99.6520 +$ +$ TOTAL MASS: 644.75600 +$ X-CG: 8.58180 +$ Y-CG: 1.05061 +$ Z-CG: 0.01840 +$ +$ +$ +$*********************************************************************** +$ ModGen is a program to set up Nastran structural, aerodynamic, and +$ optimization models for aircraft structures like wing and fuselage +$ including optional pre-sizing. +$ +$ ModGen is developed by: +$ +$ DLR German Aerospace Center +$ Institute of Aeroelasticity +$ 37073 Goettingen +$ Internet www.dlr.de/ae +$ Contact: thomas.klimmek@dlr.de +$ +$*********************************************************************** +$ +$ diff --git a/doc/tutorials/DC3_model/fem/nonstructural_masses/fueltanks_low_mi.SCONM2_mrd b/doc/tutorials/DC3_model/fem/nonstructural_masses/fueltanks_low_mi.SCONM2_mrd new file mode 100644 index 00000000..815cfd52 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/nonstructural_masses/fueltanks_low_mi.SCONM2_mrd @@ -0,0 +1,36 @@ +$ +$*********************************************************************** +$ Created by ModGen (Version 2.2401.01 (latest version)) +$ Operating System: Linux +$ Date & Time: 2024-02-21 15:41:39 +$*********************************************************************** +$ +$ +$----------------------------------------------------------------------- +$ +CONM2 8000154090001 0 161.473.0145699-.263119-.156335 + ++ 14.7777 -5.6-14 92.9288 .309980 4.39-16 100.509 +CONM2 8000254090003 0 161.466.0145699 .263094-.156336 + ++ 14.7761 3.980-6 92.9245 .309960 4.370-5 100.502 +CONM2 8000354090003 0 161.473.0145699-.263110-.156335 + ++ 14.7777 7.970-6 92.9291 .309980 8.740-5 100.509 +CONM2 8000454090004 0 160.344.0145699-.261260-.156335 + ++ 14.5198 7.919-6 92.2792 .307810 8.679-5 99.6520 +$ +$ +$*********************************************************************** +$ ModGen is a program to set up Nastran structural, aerodynamic, and +$ optimization models for aircraft structures like wing and fuselage +$ including optional pre-sizing. +$ +$ ModGen is developed by: +$ +$ DLR German Aerospace Center +$ Institute of Aeroelasticity +$ 37073 Goettingen +$ Internet www.dlr.de/ae +$ Contact: thomas.klimmek@dlr.de +$ +$*********************************************************************** +$ +$ diff --git a/doc/tutorials/DC3_model/fem/nonstructural_masses/payload.csv b/doc/tutorials/DC3_model/fem/nonstructural_masses/payload.csv new file mode 100644 index 00000000..bd40d5ce --- /dev/null +++ b/doc/tutorials/DC3_model/fem/nonstructural_masses/payload.csv @@ -0,0 +1,19 @@ +$ Mono, 10pt, column width 17mm, no spacing +$2345678 + +$payloadnodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 777001 5.302 0.000 0.000 +$ GRID 777002 10.128 0.000 0.000 +$ GRID 777003 14.954 0.000 0.000 + + +$payloadmass model +$crew eq EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 707001 100003 -1 746.000 5.302 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 707002 100005 -11543.000 10.128 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 707003 100008 -1 231.000 14.954 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 diff --git a/doc/tutorials/DC3_model/fem/nonstructural_masses/payload_modified.csv b/doc/tutorials/DC3_model/fem/nonstructural_masses/payload_modified.csv new file mode 100644 index 00000000..027566e7 --- /dev/null +++ b/doc/tutorials/DC3_model/fem/nonstructural_masses/payload_modified.csv @@ -0,0 +1,19 @@ +$ Mono, 10pt, column width 17mm, no spacing +$2345678 + +$payloadnodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 777001 5.302 0.000 0.000 +$ GRID 777002 10.128 0.000 0.000 +$ GRID 777003 14.954 0.000 0.000 + + +$payloadmass model +$crew eq EID G CID M X1 X2 X3 + +$ I11 I21 I22 I31 I32 I33 +CONM2 707001 100003 -1 746.000 10.128 0.000 0.000 + + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 707002 100005 -11543.000 14.954 0.000 0.000 + + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 707003 100008 -1 231.000 14.954 0.000 0.000 + + 0.000 0.000 0.000 0.000 0.000 0.000 diff --git a/doc/tutorials/DC3_model/fem/nonstructural_masses/systems-mass.csv b/doc/tutorials/DC3_model/fem/nonstructural_masses/systems-mass.csv new file mode 100644 index 00000000..5b72093b --- /dev/null +++ b/doc/tutorials/DC3_model/fem/nonstructural_masses/systems-mass.csv @@ -0,0 +1,99 @@ +$ Mono, 10pt, column width 17mm, no spacing +$2345678 + +$ LG nodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 101001 7.8180 2.745 0.000 +$ GRID 101002 7.8180 -2.745 0.000 +$ GRID 101003 18.4670 0.000 0.878 + + +$ LG mass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 12000164090006 -1 139.815 7.8180 2.745 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 12000254090006 -1 139.815 7.8180 -2.745 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 120003 100010 -1 31.070 18.4670 0.000 0.878 + 0.000 0.000 0.000 0.000 0.000 0.000 + + +$FuelSysnodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 102001 8.0380 1.867 0.000 +$ GRID 102002 8.0380 -1.867 0.000 + + +$FuelSysmass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 13000164090005 -1 103.550 8.0380 1.867 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 +CONM2 13000254090005 -1 103.550 8.0380 -1.867 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 + +$FltCtl nodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 103001 5.138 0.000 1.427 + +$FltCtl mass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 140001 100003 -1 241.900 5.138 0.000 1.427 + 0.000 0.000 0.000 0.000 0.000 0.000 + + +$HydSys nodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 104001 5.138 0.000 1.427 + +$HydSys mass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 150001 100003 -1 12.500 5.138 0.000 1.427 + 0.000 0.000 0.000 0.000 0.000 0.000 + + +$Avioni nodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 105001 3.293 0.000 0.000 + +$Avioni mass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 160001 100002 -1 142.300 3.293 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 + + +$EleSys nodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 106001 3.293 0.000 0.000 + +$EleSys mass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 170001 100002 -1 255.900 3.293 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 + + +$AirCon nodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 107001 7.112 0.000 0.000 + +$AirCon mass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 180001 100004 -1 608.000 7.112 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 + + +$Furnishnodes ref is 2meters in frontof nose +$ GRID ID CP X1 X2 X3 CD +$ GRID 108001 7.965 0.000 0.000 + +$Furnishmass model +$ CONM2 EID G CID M X1 X2 X3 +$ I11 I21 I22 I31 I32 I33 +CONM2 190001 100004 -1 696.300 7.965 0.000 0.000 + 0.000 0.000 0.000 0.000 0.000 0.000 diff --git a/doc/tutorials/first_steps.ipynb b/doc/tutorials/first_steps.ipynb index 8e64eafa..9bbc221f 100644 --- a/doc/tutorials/first_steps.ipynb +++ b/doc/tutorials/first_steps.ipynb @@ -42,7 +42,11 @@ "cell_type": "code", "execution_count": null, "id": "744e8fab", - "metadata": {}, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ "k = program_flow.Kernel('jcl_dc3_firststeps', pre=True, main=False, post=False,\n", @@ -118,7 +122,11 @@ "cell_type": "code", "execution_count": null, "id": "3c6bf524", - "metadata": {}, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ "k = program_flow.Kernel(job_name='jcl_dc3_firststeps', pre=False, main=True, post=False,\n", @@ -176,7 +184,11 @@ "cell_type": "code", "execution_count": null, "id": "1151d9ec", - "metadata": {}, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ "k = program_flow.Kernel(job_name='jcl_dc3_firststeps', pre=False, main=False, post=True,\n", diff --git a/doc/tutorials/flutter.ipynb b/doc/tutorials/flutter.ipynb index 0e8a2223..f6e17bdc 100644 --- a/doc/tutorials/flutter.ipynb +++ b/doc/tutorials/flutter.ipynb @@ -153,7 +153,11 @@ "cell_type": "code", "execution_count": null, "id": "a2aab232-5e9d-4260-b07e-8ec160b863c3", - "metadata": {}, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ "from loadskernel import program_flow\n", @@ -208,7 +212,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.12" + "version": "3.13.11" } }, "nbformat": 4, diff --git a/doc/tutorials/gust_time_domain.ipynb b/doc/tutorials/gust_time_domain.ipynb index b79ea07d..9176e0a7 100644 --- a/doc/tutorials/gust_time_domain.ipynb +++ b/doc/tutorials/gust_time_domain.ipynb @@ -114,7 +114,10 @@ "execution_count": null, "id": "29800512-fa3e-408d-b698-2c37a048fa55", "metadata": { - "scrolled": true + "scrolled": true, + "tags": [ + "hide-output" + ] }, "outputs": [], "source": [ diff --git a/doc/tutorials/maneuver_loads.ipynb b/doc/tutorials/maneuver_loads.ipynb index c849693e..5dc47efb 100644 --- a/doc/tutorials/maneuver_loads.ipynb +++ b/doc/tutorials/maneuver_loads.ipynb @@ -203,7 +203,11 @@ "cell_type": "code", "execution_count": null, "id": "38c976bf-f470-4c0f-b41e-c0c07d739e85", - "metadata": {}, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ "from loadskernel import program_flow\n", diff --git a/doc/tutorials/trim.ipynb b/doc/tutorials/trim.ipynb index 67e1fa84..30b07eb0 100644 --- a/doc/tutorials/trim.ipynb +++ b/doc/tutorials/trim.ipynb @@ -26,7 +26,11 @@ "cell_type": "code", "execution_count": null, "id": "e593fcac-7661-451c-ab04-2e9ed077362c", - "metadata": {}, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ "from loadskernel import program_flow\n", diff --git a/loadscompare/compare.py b/loadscompare/compare.py index 0d4afb10..b489f704 100644 --- a/loadscompare/compare.py +++ b/loadscompare/compare.py @@ -38,6 +38,31 @@ def __init__(self): self.file_opt['initialdir'] = os.getcwd() self.file_opt['title'] = 'Load Monstations' + # GUI attributes for Loads tab + self.lb_dataset = None + self.lb_mon = None + self.cb_color = None + self.cb_xaxis = None + self.cb_yaxis = None + self.cb_hull = None + self.cb_labels = None + self.cb_minmax = None + self.label_n_loadcases = None + + # GUI attributes for Time tab + self.label_dataset_time = None + self.label_monstation_time = None + self.lb_subcases_time = None + self.lb_dof_time = None + + # Other GUI attributes + self.container = None + self.tabs_widget = None + self.canvas = None + self.toolbar = None + self.plotting = None + self.window = None + def run(self): # Create the app. app = self.initApp() @@ -88,6 +113,10 @@ def initTabs(self): # Add tabs self.initLoadsTab() + self.initTimeTab() + + # Connect tab change signal + self.tabs_widget.currentChanged.connect(self.on_tab_changed) def initLoadsTab(self): tab_loads = QWidget() @@ -95,11 +124,11 @@ def initLoadsTab(self): # Elements of loads tab self.lb_dataset = QListWidget() self.lb_dataset.setSelectionMode(QAbstractItemView.ExtendedSelection) - self.lb_dataset.itemSelectionChanged.connect(self.show_choice) + self.lb_dataset.itemSelectionChanged.connect(self.on_selection_changed) self.lb_dataset.itemChanged.connect(self.update_desc) self.lb_mon = QListWidget() - self.lb_mon.itemSelectionChanged.connect(self.show_choice) + self.lb_mon.itemSelectionChanged.connect(self.on_selection_changed) self.cb_color = QComboBox() self.cb_color.addItems(self.colors) @@ -108,24 +137,24 @@ def initLoadsTab(self): self.cb_xaxis = QComboBox() self.cb_xaxis.addItems(self.dof) self.cb_xaxis.setCurrentIndex(3) - self.cb_xaxis.activated.connect(self.show_choice) + self.cb_xaxis.activated.connect(self.on_selection_changed) self.cb_yaxis = QComboBox() self.cb_yaxis.addItems(self.dof) self.cb_yaxis.setCurrentIndex(4) - self.cb_yaxis.activated.connect(self.show_choice) + self.cb_yaxis.activated.connect(self.on_selection_changed) self.cb_hull = QCheckBox("show convex hull") self.cb_hull.setChecked(False) - self.cb_hull.stateChanged.connect(self.show_choice) + self.cb_hull.stateChanged.connect(self.on_selection_changed) self.cb_labels = QCheckBox("show labels") self.cb_labels.setChecked(False) - self.cb_labels.stateChanged.connect(self.show_choice) + self.cb_labels.stateChanged.connect(self.on_selection_changed) self.cb_minmax = QCheckBox("show min/max") self.cb_minmax.setChecked(False) - self.cb_minmax.stateChanged.connect(self.show_choice) + self.cb_minmax.stateChanged.connect(self.on_selection_changed) self.label_n_loadcases = QLabel() @@ -143,6 +172,33 @@ def initLoadsTab(self): layout.addWidget(self.cb_minmax, 6, 0, 1, 2) layout.addWidget(self.label_n_loadcases, 7, 0, 1, 2) + def initTimeTab(self): + tab_time = QWidget() + self.tabs_widget.addTab(tab_time, 'Time') + # Elements of time tab + self.label_dataset_time = QLabel() + self.label_dataset_time.setText('Dataset: ') + + self.label_monstation_time = QLabel() + self.label_monstation_time.setText('Monstation: ') + + self.lb_subcases_time = QListWidget() + self.lb_subcases_time.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.lb_subcases_time.itemSelectionChanged.connect(self.update_time_plot) + + self.lb_dof_time = QListWidget() + self.lb_dof_time.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.lb_dof_time.itemSelectionChanged.connect(self.update_time_plot) + self.lb_dof_time.addItems(self.dof) + self.lb_dof_time.setCurrentRow(3) + + layout = QGridLayout(tab_time) + # Notation: layout.addWidget(widget, row, column, rowSpan, columnSpan) + layout.addWidget(self.label_dataset_time, 0, 0, 1, 2) + layout.addWidget(self.label_monstation_time, 1, 0, 1, 2) + layout.addWidget(self.lb_subcases_time, 2, 0, 1, 1) + layout.addWidget(self.lb_dof_time, 3, 0, 1, 1) + def initMatplotlibFigure(self): # init Matplotlib Plot fig1 = Figure() @@ -194,7 +250,7 @@ def initWindow(self): self.window.setWindowTitle("Loads Compare") self.window.show() - def show_choice(self, *args): + def on_selection_changed(self): # called on change in listbox, combobox, etc # discard extra variables if len(self.lb_dataset.selectedItems()) == 1: @@ -202,17 +258,17 @@ def show_choice(self, *args): self.cb_color.setEnabled(True) else: self.cb_color.setDisabled(True) - self.update_plot() + self.update_loads_plot() def update_color(self, color): self.datasets['color'][self.lb_dataset.currentRow()] = self.colors[color] - self.update_plot() + self.update_loads_plot() - def update_desc(self, *args): + def update_desc(self): self.datasets['desc'][self.lb_dataset.currentRow()] = self.lb_dataset.currentItem().text() - self.update_plot() + self.update_loads_plot() - def update_plot(self): + def update_loads_plot(self): if self.lb_dataset.currentItem() is not None and self.lb_mon.currentItem() is not None: # Get the items selected by the user. mon_sel = self.common_monstations[self.lb_mon.currentRow()] @@ -241,7 +297,7 @@ def update_plot(self): n_subcases = [len(dataset[mon_sel]['subcases']) for dataset in datasets] self.label_n_loadcases.setText(f'Selected load case: {np.sum(n_subcases)}') else: - self.plotting.plot_nothing() + self.plotting.clear_figure() self.canvas.draw() def merge_monstation(self): @@ -251,7 +307,7 @@ def merge_monstation(self): for x in [item.row() for item in self.lb_dataset.selectedIndexes()]: print(f'Working on {self.datasets['desc'][x]} ...') for station in self.common_monstations: - if station not in new_dataset.keys(): + if station not in new_dataset: # create (empty) entries for new monstation new_dataset[station] = {'CD': self.datasets['dataset'][x][station]['CD'][()], 'CP': self.datasets['dataset'][x][station]['CP'][()], @@ -272,7 +328,7 @@ def merge_monstation(self): self.datasets['desc'].append('dataset ' + str(self.datasets['n'])) self.datasets['n'] += 1 # Update fields. - self.update_fields() + self.update_fields_of_loads_tab() def load_monstation(self): # open file dialog @@ -297,7 +353,7 @@ def load_monstation(self): self.datasets['desc'].append('dataset ' + str(self.datasets['n'])) self.datasets['n'] += 1 # update fields - self.update_fields() + self.update_fields_of_loads_tab() self.file_opt['initialdir'] = os.path.split(filename)[0] def save_monstation(self): @@ -317,7 +373,7 @@ def save_monstation(self): if filename != '' and '.hdf5' in filename: data_handling.dump_hdf5(filename, dataset_sel) - def update_fields(self): + def update_fields_of_loads_tab(self): self.lb_dataset.clear() for desc in self.datasets['desc']: item = QListWidgetItem(desc) @@ -332,6 +388,52 @@ def update_fields(self): for x in self.common_monstations: self.lb_mon.addItem(QListWidgetItem(x)) + def update_time_plot(self): + # called on change in listbox + if self.lb_subcases_time.currentItem() is not None and self.lb_dof_time.currentItem() is not None: + # Get the items selected by the user. + mon_sel = self.common_monstations[self.lb_mon.currentRow()] + dataset_idx = self.lb_dataset.currentRow() + dataset_sel = self.datasets['dataset'][dataset_idx] + monstation = dataset_sel[mon_sel] + subcases_sel = [item.text() for item in self.lb_subcases_time.selectedItems()] + dofs_text = [item.text() for item in self.lb_dof_time.selectedItems()] + dofs_idx = [item.row() for item in self.lb_dof_time.selectedIndexes()] + # Call the plotting function. + self.plotting.timehistories(monstation, subcases_sel, dofs_idx, dofs_text) + else: + self.plotting.clear_figure() + self.canvas.draw() + + def on_tab_changed(self, index): + # Called when a tab is changed. + if index == 1: + # Loads tab is at index 0 + # Time tab is at index 1 + self.update_fields_of_time_tab() + + def update_fields_of_time_tab(self): + # Update the Time tab fields when it is activated. + if self.lb_dataset.currentItem() is not None and self.lb_mon.currentItem() is not None: + # Get the items selected by the user. + mon_sel = self.common_monstations[self.lb_mon.currentRow()] + dataset_idx = self.lb_dataset.currentRow() + dataset_sel = self.datasets['dataset'][dataset_idx] + + # Update the dataset label + dataset_name = self.datasets['desc'][dataset_idx] + self.label_dataset_time.setText(f'Dataset: {dataset_name}') + + # Update the monstation label + self.label_monstation_time.setText(f'Monstation: {mon_sel}') + + # Populate list of subcases by finding all integers in monstation.keys() + monstation = dataset_sel[mon_sel] + subcase_keys = [key for key in monstation if key.isdigit()] + subcase_keys.sort(key=int) + self.lb_subcases_time.clear() + self.lb_subcases_time.addItems(subcase_keys) + def command_line_interface(): c = Compare() diff --git a/loadscompare/graphics/LK_logo2.png b/loadscompare/graphics/LK_logo2.png deleted file mode 100644 index 34eca6ce..00000000 Binary files a/loadscompare/graphics/LK_logo2.png and /dev/null differ diff --git a/loadscompare/plotting.py b/loadscompare/plotting.py index 43e2e25e..01c0bd5c 100644 --- a/loadscompare/plotting.py +++ b/loadscompare/plotting.py @@ -11,30 +11,67 @@ class Plotting(plotting_standard.LoadPlots): def __init__(self, fig): plt.rcParams.update({'font.size': 16, 'svg.fonttype': 'none'}) - self.subplot = fig.add_axes([0.2, 0.15, 0.7, 0.75]) # List is [left, bottom, width, height] - im = plt.imread(os.path.dirname(__file__) + '/graphics/LK_logo2.png') - newax = fig.add_axes([0.04, 0.02, 0.10, 0.08]) + self.fig = fig + self.subplot = None + self.crit_trimcases = [] + + def clear_figure(self): + self.fig.clf() + self.subplot = None + # Add the logo in the bottom left corner. + im = plt.imread(os.path.dirname(__file__) + '/../graphics/LK_logo2.png') + newax = self.fig.add_axes([0.04, 0.02, 0.10, 0.08]) newax.imshow(im, interpolation='hanning') newax.axis('off') - def plot_nothing(self): - self.subplot.cla() - - def potato_plots(self, dataset_sel, station, descs, colors, dof_xaxis, dof_yaxis, var_xaxis, var_yaxis, + def potato_plots(self, dataset_sel, station, descs, colors, + dof_xaxis, dof_yaxis, var_xaxis, var_yaxis, show_hull, show_labels, show_minmax): + if self.subplot is None: + # Create a single axes that fills most of the figure, leaving space for the logo and labels. + # Store the axes in the class so that it can be used by potato_plot. + self.clear_figure() + self.subplot = self.fig.add_axes([0.2, 0.15, 0.7, 0.75]) # List is [left, bottom, width, height] + else: + self.subplot.cla() # This function relies on the potato plotting function in LK imported above to avoid code duplications. - # The labels, margins, etc. are adjusted in this function to fit the window space. - self.subplot.cla() - for i_dataset in range(len(dataset_sel)): + for i, dataset in enumerate(dataset_sel): self.crit_trimcases = [] - self.add_monstations(dataset_sel[i_dataset]) - self.potato_plot(station, descs[i_dataset], colors[i_dataset], dof_xaxis, dof_yaxis, + self.add_monstations(dataset) + self.potato_plot(station, descs[i], colors[i], dof_xaxis, dof_yaxis, show_hull, show_labels, show_minmax) - self.subplot.legend(loc='best') - self.subplot.ticklabel_format(style='sci', axis='x', scilimits=(-2, 2)) - self.subplot.ticklabel_format(style='sci', axis='y', scilimits=(-2, 2)) - self.subplot.grid(True) - yax = self.subplot.get_yaxis() - yax.set_label_coords(x=-0.18, y=0.5) - self.subplot.set_xlabel(var_xaxis) - self.subplot.set_ylabel(var_yaxis) + # The labels, margins, etc. are adjusted in this function to fit the window space. + a = self.subplot + a.legend(loc='best') + a.ticklabel_format(style='sci', axis='x', scilimits=(-2, 2)) + a.ticklabel_format(style='sci', axis='y', scilimits=(-2, 2)) + a.grid(True) + a.get_yaxis().set_label_coords(x=-0.18, y=0.5) + a.set_xlabel(var_xaxis) + a.set_ylabel(var_yaxis) + + def timehistories(self, monstation, subcases, dofs_idx, dofs_text): + # Clear the figure, then create subplots for each state to plot, sharing the x-axis (time). + # This is necessary as the number of axes can change, depending on the selection by the user. + self.clear_figure() + ax = self.fig.subplots(len(dofs_idx), sharex=True) + # Make sure that ax is always iterable, even if there is only one state to plot. + if len(dofs_idx) == 1: + ax = [ax] + # Plotting the time histories for each dof and subcase. + for a, dof_idx, dof_text in zip(ax, dofs_idx, dofs_text): + for subcase in subcases: + time = monstation[subcase]['t'][()] + data = monstation[subcase]['loads'][:, dof_idx] + a.plot(time, data, label=subcase) + # Format current axis. + a.ticklabel_format(style='sci', axis='y', scilimits=(-2, 2)) + a.grid(True) + a.set_ylabel(dof_text) + # Push left bound to the right to make space for the ylabel. + left, bottom, width, height = a.get_position().bounds + a.set_position([left + 0.05, bottom, width - 0.05, height]) + a.get_yaxis().set_label_coords(x=-0.18, y=0.5) + # Show legend per plot + a.legend(loc='upper right') + ax[-1].set_xlabel('Time [s]') diff --git a/loadskernel/cfd_interfaces/su2_interface.py b/loadskernel/cfd_interfaces/su2_interface.py index b6e85808..f453a38f 100644 --- a/loadskernel/cfd_interfaces/su2_interface.py +++ b/loadskernel/cfd_interfaces/su2_interface.py @@ -75,10 +75,10 @@ def __init__(self, solution): if "pysu2" in sys.modules and "SU2" in sys.modules: # make sure that all processes are at the same stage self.comm.barrier() - logging.info('Init CFD interface of type "{}" on MPI process {}.'.format(self.__class__.__name__, self.myid)) + logging.info('Init CFD interface of type "%s" on MPI process %s.', self.__class__.__name__, self.myid) else: - logging.error('pysu2 was/could NOT be imported! Model equations of type "{}" will NOT work.'.format( - self.jcl.aero['method'])) + logging.error('pysu2 was/could NOT be imported! Model equations of type "%s" will NOT work.', + self.jcl.aero['method']) self.FluidSolver = None # Set-up file system structure @@ -86,11 +86,13 @@ def __init__(self, solution): check_cfd_folders(self.jcl) check_para_path(self.jcl) copy_para_file(self.jcl, self.trimcase) - self.para_filename = self.jcl.aero['para_path'] + 'para_subcase_{}'.format(self.trimcase['subcase']) + self.para_filename = self.jcl.aero['para_path'] + f'para_subcase_{self.trimcase["subcase"]}' - # Storage for the euler transofmation of the unsteady interface + # Init some variables self.XYZ = None self.PhiThetaPsi = None + self.Ucfd = None + self.local_mesh = None def prepare_meshdefo(self, Uf, Ux2): """ @@ -143,17 +145,16 @@ def update_general_para(self): # set general parameters, which don't change over the course of the CFD simulation, so they are only updated # for the first execution config['MESH_FILENAME'] = self.jcl.meshdefo['surface']['filename_grid'] - config['RESTART_FILENAME'] = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}.dat'.format( - self.trimcase['subcase']) - config['SOLUTION_FILENAME'] = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}.dat'.format( - self.trimcase['subcase']) - config['SURFACE_FILENAME'] = self.jcl.aero['para_path'] + 'sol/surface_subcase_{}'.format( - self.trimcase['subcase']) - config['VOLUME_FILENAME'] = self.jcl.aero['para_path'] + 'sol/volume_subcase_{}'.format( - self.trimcase['subcase']) - config['CONV_FILENAME'] = self.jcl.aero['para_path'] + 'sol/history_subcase_{}'.format( - self.trimcase['subcase']) - # free stream definition + config['RESTART_FILENAME'] = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}.dat' + config['SOLUTION_FILENAME'] = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}.dat' + config['SURFACE_FILENAME'] = self.jcl.aero['para_path'] + f'sol/surface_subcase_{self.trimcase["subcase"]}' + config['VOLUME_FILENAME'] = self.jcl.aero['para_path'] + f'sol/volume_subcase_{self.trimcase["subcase"]}' + config['CONV_FILENAME'] = self.jcl.aero['para_path'] + f'sol/history_subcase_{self.trimcase["subcase"]}' + # Set density-based free-stream initialization and use the thermodynamics quantities instead of + # the reynolds number. + config['FREESTREAM_OPTION'] = 'DENSITY_FS' + config['INIT_OPTION'] = 'TD_CONDITIONS' + # Free-stream definition config['FREESTREAM_TEMPERATURE'] = self.atmo['T'] config['FREESTREAM_DENSITY'] = self.atmo['rho'] config['FREESTREAM_PRESSURE'] = self.atmo['p'] @@ -172,9 +173,8 @@ def update_general_para(self): config['MARKER_DEFORM_MESH'] = '( ' + ', '.join(self.jcl.meshdefo['surface']['markers']) + ' )' # activate grid movement config['GRID_MOVEMENT'] = 'ROTATING_FRAME' - config['MOTION_ORIGIN'] = '{} {} {}'.format(self.cggrid['offset'][0, 0], - self.cggrid['offset'][0, 1], - self.cggrid['offset'][0, 2]) + config['MOTION_ORIGIN'] = \ + f'{self.cggrid["offset"][0, 0]} {self.cggrid["offset"][0, 1]} {self.cggrid["offset"][0, 2]}' config['MACH_MOTION'] = self.trimcase['Ma'] # there is no restart for the first execution config['RESTART_SOL'] = 'NO' @@ -201,7 +201,7 @@ def init_solver(self): def run_solver(self, i_timestep=0): logging.debug('Waiting until all processes are ready to perform a coordinated start...') self.comm.barrier() - logging.info('Launch SU2 for time step {}.'.format(i_timestep)) + logging.info('Launch SU2 for time step %s.', i_timestep) # start timer t_start = time.time() # initialize SU2 if this is the first run. @@ -218,7 +218,7 @@ def run_solver(self, i_timestep=0): self.FluidSolver.Output(i_timestep) self.comm.barrier() - logging.debug('CFD computation performed in {:.2f} seconds.'.format(time.time() - t_start)) + logging.debug('CFD computation performed in %.2f seconds.', time.time() - t_start) def get_last_solution(self): return self.Pcfd_global() @@ -234,7 +234,7 @@ def Pcfd_global(self): self.comm.barrier() self.comm.Allgatherv(Pcfd_send, Pcfd_rcv) Pcfd = Pcfd_rcv.sum(axis=0) - logging.debug('All nodal loads recovered, sorted and gathered in {:.2f} sec.'.format(time.time() - t_start)) + logging.debug('All nodal loads recovered, sorted and gathered in %.2f sec.', time.time() - t_start) return Pcfd def prepare_initial_solution(self): @@ -299,7 +299,7 @@ def get_local_mesh(self): 'set_global': np.array(tmp_set_global).squeeze(), 'n': n } - logging.debug('This is process {} and my local mesh has a size of {}'.format(self.myid, self.local_mesh['n'])) + logging.debug('This is process %s and my local mesh has a size of %s', self.myid, self.local_mesh['n']) def transfer_deformations(self, grid_i, U_i, set_i, rbf_type, surface_spline, support_radius=2.0): """ @@ -307,7 +307,7 @@ def transfer_deformations(self, grid_i, U_i, set_i, rbf_type, surface_spline, su This version works on the local mesh of a mpi partition, making the calculation of the mesh deformations faster. """ - logging.info('Transferring deformations to the local CFD surface with {} nodes.'.format(self.local_mesh['n'])) + logging.info('Transferring deformations to the local CFD surface with %s nodes.', self.local_mesh['n']) # build spline matrix PHIi_d = spline_functions.spline_rbf(grid_i, set_i, self.local_mesh, '', rbf_type=rbf_type, surface_spline=surface_spline, @@ -419,17 +419,16 @@ def update_general_para(self): # set general parameters, which don't change over the course of the CFD simulation, so they are only updated # for the first execution config['MESH_FILENAME'] = self.jcl.meshdefo['surface']['filename_grid'] - config['RESTART_FILENAME'] = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}.dat'.format( - self.trimcase['subcase']) - config['SOLUTION_FILENAME'] = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}.dat'.format( - self.trimcase['subcase']) - config['SURFACE_FILENAME'] = self.jcl.aero['para_path'] + 'sol/surface_subcase_{}'.format( - self.trimcase['subcase']) - config['VOLUME_FILENAME'] = self.jcl.aero['para_path'] + 'sol/volume_subcase_{}'.format( - self.trimcase['subcase']) - config['CONV_FILENAME'] = self.jcl.aero['para_path'] + 'sol/history_subcase_{}'.format( - self.trimcase['subcase']) - # free stream definition + config['RESTART_FILENAME'] = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}.dat' + config['SOLUTION_FILENAME'] = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}.dat' + config['SURFACE_FILENAME'] = self.jcl.aero['para_path'] + f'sol/surface_subcase_{self.trimcase["subcase"]}' + config['VOLUME_FILENAME'] = self.jcl.aero['para_path'] + f'sol/volume_subcase_{self.trimcase["subcase"]}' + config['CONV_FILENAME'] = self.jcl.aero['para_path'] + f'sol/history_subcase_{self.trimcase["subcase"]}' + # Set density-based free-stream initialization and use the thermodynamics quantities instead of + # the reynolds number. + config['FREESTREAM_OPTION'] = 'DENSITY_FS' + config['INIT_OPTION'] = 'TD_CONDITIONS' + # Free-stream definition config['FREESTREAM_TEMPERATURE'] = self.atmo['T'] config['FREESTREAM_DENSITY'] = self.atmo['rho'] config['FREESTREAM_PRESSURE'] = self.atmo['p'] @@ -486,12 +485,9 @@ def update_timedom_para(self): config['RESTART_SOL'] = 'YES' config['RESTART_ITER'] = 2 # create links for the .dat files... - filename_steady = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}.dat'.format( - self.trimcase['subcase']) - filename_unsteady0 = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}_00000.dat'.format( - self.trimcase['subcase']) - filename_unsteady1 = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}_00001.dat'.format( - self.trimcase['subcase']) + filename_steady = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}.dat' + filename_unsteady0 = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}_00000.dat' + filename_unsteady1 = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}_00001.dat' try: os.symlink(filename_steady, filename_unsteady0) os.symlink(filename_steady, filename_unsteady1) @@ -499,12 +495,9 @@ def update_timedom_para(self): # Do nothing, the most likely cause is that the file already exists. pass # ...and for the .csv files - filename_steady = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}.csv'.format( - self.trimcase['subcase']) - filename_unsteady0 = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}_00000.csv'.format( - self.trimcase['subcase']) - filename_unsteady1 = self.jcl.aero['para_path'] + 'sol/restart_subcase_{}_00001.csv'.format( - self.trimcase['subcase']) + filename_steady = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}.csv' + filename_unsteady0 = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}_00000.csv' + filename_unsteady1 = self.jcl.aero['para_path'] + f'sol/restart_subcase_{self.trimcase["subcase"]}_00001.csv' try: os.symlink(filename_steady, filename_unsteady0) os.symlink(filename_steady, filename_unsteady1) @@ -562,9 +555,8 @@ def update_gust_para(self, Vtas, Vgust): config['GUST_DIR'] = 'Y_DIR' config['GUST_AMPL'] = -Vgust else: - logging.error('Gust orientation {} currently NOT supported by SU2. \ - Possible values: 0.0, 90.0, 180.0, 270.0 or 360.0 degrees.'.format( - self.simcase['gust_orientation'])) + logging.error('Gust orientation %s currently NOT supported by SU2. \ + Possible values: 0.0, 90.0, 180.0, 270.0 or 360.0 degrees.', self.simcase['gust_orientation']) # Note: In SU2 this is the full gust length, not the gust gradient H (half gust length). config['GUST_WAVELENGTH'] = 2.0 * self.simcase['gust_gradient'] config['GUST_PERIODS'] = 1.0 diff --git a/loadskernel/fem_interfaces/fem_helper.py b/loadskernel/fem_interfaces/fem_helper.py index ba51e523..0378e77a 100644 --- a/loadskernel/fem_interfaces/fem_helper.py +++ b/loadskernel/fem_interfaces/fem_helper.py @@ -89,3 +89,10 @@ def check_matrix_symmetry(matrix): matrix_sym = force_matrix_symmetry(matrix) result = (matrix != matrix_sym).nnz == 0 return result + + +def check_matrix_symmetry_allclose(matrix): + # Check if a matrix is symmetric by forcing symmetry and then compare with the original matrix. + matrix_sym = force_matrix_symmetry(matrix) + result = np.allclose(matrix.toarray(), matrix_sym.toarray()) + return result diff --git a/loadskernel/fem_interfaces/nastran95_interface.py b/loadskernel/fem_interfaces/nastran95_interface.py new file mode 100644 index 00000000..1f910985 --- /dev/null +++ b/loadskernel/fem_interfaces/nastran95_interface.py @@ -0,0 +1,47 @@ +# Built-ins +import logging + +# Libs +from scipy.sparse import csc_matrix + +# Own modules +from loadskernel.fem_interfaces.nastran_interface import NastranInterface +from loadskernel.io_functions import read_op2 + + +class Nastran95Interface(NastranInterface): + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.KGG: csc_matrix = csc_matrix((0, 0)) + self.GM: csc_matrix = csc_matrix((0, 0)) + self.MGG: csc_matrix = csc_matrix((0, 0)) + self.i_mass: int = 0 + self.op2_geom = None + + def get_stiffness_matrix(self): + # Use internal op2 reader. The matrices are numerically eqivalent compared with pyNastran's op2 reader (tested with the + # DC3 model using np.allclose). + if 'filename_op2' in self.jcl.geom: + self.op2_geom = read_op2.read_op2(self.jcl.geom['filename_op2']) + # Convert to sparse foramt. + self.KGG = csc_matrix(self.op2_geom['KGG']) + self.GM = csc_matrix(self.op2_geom['GM'].T) + else: + logging.error('Please provide filename of .op2 file containing matrices Kgg and GM.') + + def get_mass_matrix(self, i_mass): + self.i_mass = i_mass + if 'filename_op2' in self.jcl.mass: + op2_mass = read_op2.read_op2(self.jcl.mass['filename_op2'][self.i_mass]) + self.MGG = csc_matrix(op2_mass['MGG']) + else: + logging.error('Please provide filename(s) of .op2 files containing the matrices Mgg.') + + return self.MGG + + def get_dofs(self): + # See if a USET table was included in the OP2 file from the geom setion (along with the stiffness matrix). + if self.op2_geom['uset'] is None: + logging.error('No USET found in OP2-file %s !', self.jcl.geom['filename_op2']) + self.get_sets_from_bitposes(self.op2_geom['uset']) diff --git a/loadskernel/fem_interfaces/nastran_interface.py b/loadskernel/fem_interfaces/nastran_interface.py index f9fd0f24..92cabdf3 100644 --- a/loadskernel/fem_interfaces/nastran_interface.py +++ b/loadskernel/fem_interfaces/nastran_interface.py @@ -69,10 +69,10 @@ def get_dofs(self): # Prepare some data required for modal analysis which is not mass case dependent. # The uset is actually geometry dependent and should go into the geometry section. # However, it is only required for modal analysis... - logging.info('Read USET from OP2-file {} ...'.format(self.jcl.geom['filename_uset'])) - op2_data = read_op2.read_post_op2(self.jcl.geom['filename_uset'], verbose=False) + logging.info('Read USET from OP2-file %s ...', self.jcl.geom['filename_uset']) + op2_data = read_op2.read_op2(self.jcl.geom['filename_uset']) if op2_data['uset'] is None: - logging.error('No USET found in OP2-file {} !'.format(self.jcl.geom['filename_uset'])) + logging.error('No USET found in OP2-file %s !', self.jcl.geom['filename_uset']) self.get_sets_from_bitposes(op2_data['uset']) def prepare_stiffness_matrices(self): diff --git a/loadskernel/io_functions/read_cfdgrids.py b/loadskernel/io_functions/read_cfdgrids.py index 941a8cad..d31c5b5a 100644 --- a/loadskernel/io_functions/read_cfdgrids.py +++ b/loadskernel/io_functions/read_cfdgrids.py @@ -1,4 +1,4 @@ -import h5py +from itertools import chain import logging import numpy as np @@ -7,158 +7,108 @@ class ReadCfdgrids: - def __init__(self, jcl): - self.jcl = jcl + def __init__(self): + self.cfdgrids = {} + self.cfdgrid = {} + + def read_surface(self, jcl): + # Pick up the filename of the grid and the markers from the jcl. + # The markers are needed to extract the points that belong to the surface(s) of interest. if 'surface' in jcl.meshdefo: - self.filename_grid = self.jcl.meshdefo['surface']['filename_grid'] - self.markers = self.jcl.meshdefo['surface']['markers'] + filename_grid = jcl.meshdefo['surface']['filename_grid'] + markers = jcl.meshdefo['surface']['markers'] + # Swich between different file formats as specified in the jcl. + if 'fileformat' in jcl.meshdefo['surface'] and jcl.meshdefo['surface']['fileformat'].lower() == 'netcdf': + self.read_netcdf(filename_grid, markers) + elif 'fileformat' in jcl.meshdefo['surface'] and jcl.meshdefo['surface']['fileformat'].lower() == 'su2': + self.read_su2(filename_grid, markers) + else: + logging.error('jcl.meshdefo["surface"]["fileformat"] must be "netcdf" or "su2"') else: logging.error('jcl.meshdefo has no key "surface"') - def read_surface(self, merge_domains=False): - if 'fileformat' in self.jcl.meshdefo['surface'] and self.jcl.meshdefo['surface']['fileformat'].lower() == 'cgns': - self.read_cfdmesh_cgns(merge_domains) - elif 'fileformat' in self.jcl.meshdefo['surface'] and self.jcl.meshdefo['surface']['fileformat'].lower() == 'netcdf': - self.read_cfdmesh_netcdf(merge_domains) - elif 'fileformat' in self.jcl.meshdefo['surface'] and self.jcl.meshdefo['surface']['fileformat'].lower() == 'su2': - self.read_cfdmesh_su2(merge_domains) - else: - logging.error('jcl.meshdefo["surface"]["fileformat"] must be "netcdf", "cgns" or "su2"') - return - - def read_cfdmesh_cgns(self, merge_domains=False): - logging.info('Extracting all points from grid {}'.format(self.filename_grid)) - f = h5py.File(self.filename_grid, 'r') - f_scale = 1.0 / 1000.0 # convert to SI units: 0.001 if mesh is given in [mm], 1.0 if given in [m] - markers = f['Base'].keys() - markers.sort() - if merge_domains: - x = np.array([]) - y = np.array([]) - z = np.array([]) - for marker in markers: - # loop over domains - if marker[:4] == 'dom-': - logging.info(' - {} included'.format(marker)) - domain = f['Base'][marker] - x = np.concatenate((x, domain['GridCoordinates']['CoordinateX'][' data'][:].reshape(-1) * f_scale)) - y = np.concatenate((y, domain['GridCoordinates']['CoordinateY'][' data'][:].reshape(-1) * f_scale)) - z = np.concatenate((z, domain['GridCoordinates']['CoordinateZ'][' data'][:].reshape(-1) * f_scale)) - else: - logging.info(' - {} skipped'.format(marker)) - f.close() - n = len(x) - # build cfdgrid - self.cfdgrid = {} - self.cfdgrid['ID'] = np.arange(n) + 1 - self.cfdgrid['CP'] = np.zeros(n) - self.cfdgrid['CD'] = np.zeros(n) - self.cfdgrid['n'] = n - self.cfdgrid['offset'] = np.vstack((x, y, z)).T - self.cfdgrid['set'] = np.arange(6 * self.cfdgrid['n']).reshape(-1, 6) - self.cfdgrid['desc'] = 'all domains' - else: - self.cfdgrids = {} - for marker in markers: - # loop over domains - if marker[:4] == 'dom-': - logging.info(' - {} included'.format(marker)) - domain = f['Base'][marker] - x = domain['GridCoordinates']['CoordinateX'][' data'][:].reshape(-1) * f_scale - y = domain['GridCoordinates']['CoordinateY'][' data'][:].reshape(-1) * f_scale - z = domain['GridCoordinates']['CoordinateZ'][' data'][:].reshape(-1) * f_scale - n = len(x) - # build cfdgrid - cfdgrid = {} - cfdgrid['ID'] = np.arange(n) + 1 - cfdgrid['CP'] = np.zeros(n) - cfdgrid['CD'] = np.zeros(n) - cfdgrid['n'] = n - cfdgrid['offset'] = np.vstack((x, y, z)).T - cfdgrid['set'] = np.arange(6 * cfdgrid['n']).reshape(-1, 6) - cfdgrid['desc'] = marker - self.cfdgrids[marker] = cfdgrid - else: - logging.info(' - {} skipped'.format(marker)) - f.close() - - def read_cfdmesh_netcdf(self, merge_domains=False): - markers = self.markers - logging.info('Extracting points belonging to marker(s) {} from grid {}'.format(str(markers), self.filename_grid)) - # --- get all points on surfaces --- - ncfile_grid = netcdf.NetCDFFile(self.filename_grid, 'r') + def read_netcdf(self, filename_grid, markers): + logging.info('Extracting points belonging to marker(s) %s from grid %s', markers, filename_grid) + # Get all points on surfaces + ncfile_grid = netcdf.netcdf_file(filename_grid, 'r') boundarymarker_surfaces = ncfile_grid.variables['boundarymarker_of_surfaces'][:] - points_of_surface = [] - # merge triangles with quadrilaterals + # Get all surface trianagles and quadrilaterals. + triangles = np.array([]) + quadrilaterals = np.array([]) if 'points_of_surfacetriangles' in ncfile_grid.variables: - points_of_surface += ncfile_grid.variables['points_of_surfacetriangles'][:].tolist() + triangles = ncfile_grid.variables['points_of_surfacetriangles'][:].copy() if 'points_of_surfacequadrilaterals' in ncfile_grid.variables: - points_of_surface += ncfile_grid.variables['points_of_surfacequadrilaterals'][:].tolist() + quadrilaterals = ncfile_grid.variables['points_of_surfacequadrilaterals'][:].copy() + # Assemble the cfdgrids, one grid for each marker + for marker in markers: + # Get points on surfaces for this marker + triangles_on_marker = triangles[boundarymarker_surfaces[:len(triangles)] == marker] + quadrilaterals_on_marker = quadrilaterals[boundarymarker_surfaces[len(triangles):] == marker] + points_on_marker = np.unique(np.concatenate((triangles_on_marker.flatten(), + quadrilaterals_on_marker.flatten()))) + cfdgrid = {} + cfdgrid['ID'] = points_on_marker + cfdgrid['CP'] = np.zeros(cfdgrid['ID'].shape) + cfdgrid['CD'] = np.zeros(cfdgrid['ID'].shape) + cfdgrid['n'] = len(cfdgrid['ID']) + cfdgrid['offset'] = np.vstack((ncfile_grid.variables['points_xc'][:][points_on_marker].copy(), + ncfile_grid.variables['points_yc'][:][points_on_marker].copy(), + ncfile_grid.variables['points_zc'][:][points_on_marker].copy())).T + cfdgrid['set'] = np.arange(6 * cfdgrid['n']).reshape(-1, 6) + cfdgrid['desc'] = str(marker) + cfdgrid['triangles'] = triangles_on_marker.tolist() + cfdgrid['quadrilaterals'] = quadrilaterals_on_marker.tolist() + self.cfdgrids[str(marker)] = cfdgrid + + # Merge all markers into one cfdgrid + points_of_all_markers = np.array([], dtype=int) + triangles_of_all_markers = [] + quadrilaterals_of_all_markers = [] + for marker, cfdgrid in self.cfdgrids.items(): + points_of_all_markers = np.concatenate((points_of_all_markers, cfdgrid['ID'])) + triangles_of_all_markers += cfdgrid['triangles'] + quadrilaterals_of_all_markers += cfdgrid['quadrilaterals'] + points_of_all_markers = np.unique(points_of_all_markers) + + self.cfdgrid['ID'] = points_of_all_markers + self.cfdgrid['CP'] = np.zeros(self.cfdgrid['ID'].shape) + self.cfdgrid['CD'] = np.zeros(self.cfdgrid['ID'].shape) + self.cfdgrid['n'] = len(self.cfdgrid['ID']) + self.cfdgrid['offset'] = np.vstack((ncfile_grid.variables['points_xc'][:][points_of_all_markers].copy(), + ncfile_grid.variables['points_yc'][:][points_of_all_markers].copy(), + ncfile_grid.variables['points_zc'][:][points_of_all_markers].copy())).T + self.cfdgrid['set'] = np.arange(6 * self.cfdgrid['n']).reshape(-1, 6) + self.cfdgrid['desc'] = markers + self.cfdgrid['triangles'] = triangles_of_all_markers + self.cfdgrid['quadrilaterals'] = quadrilaterals_of_all_markers - if merge_domains: - # --- get points on surfaces according to marker --- - surfaces = np.array([], dtype=int) - for marker in markers: - surfaces = np.hstack((surfaces, np.where(boundarymarker_surfaces == marker)[0])) - points = np.unique([points_of_surface[s] for s in surfaces]) - # build cfdgrid - self.cfdgrid = {} - self.cfdgrid['ID'] = points - self.cfdgrid['CP'] = np.zeros(self.cfdgrid['ID'].shape) - self.cfdgrid['CD'] = np.zeros(self.cfdgrid['ID'].shape) - self.cfdgrid['n'] = len(self.cfdgrid['ID']) - self.cfdgrid['offset'] = np.vstack((ncfile_grid.variables['points_xc'][:][points].copy(), - ncfile_grid.variables['points_yc'][:][points].copy(), - ncfile_grid.variables['points_zc'][:][points].copy())).T - self.cfdgrid['set'] = np.arange(6 * self.cfdgrid['n']).reshape(-1, 6) - self.cfdgrid['desc'] = markers - self.cfdgrid['points_of_surface'] = [points_of_surface[s] for s in surfaces] - else: - self.cfdgrids = {} - for marker in markers: - # --- get points on surfaces according to marker --- - surfaces = np.where(boundarymarker_surfaces == marker)[0] - points = np.unique([points_of_surface[s] for s in surfaces]) - # build cfdgrid - cfdgrid = {} - cfdgrid['ID'] = points - cfdgrid['CP'] = np.zeros(cfdgrid['ID'].shape) - cfdgrid['CD'] = np.zeros(cfdgrid['ID'].shape) - cfdgrid['n'] = len(cfdgrid['ID']) - cfdgrid['offset'] = np.vstack((ncfile_grid.variables['points_xc'][:][points].copy(), - ncfile_grid.variables['points_yc'][:][points].copy(), - ncfile_grid.variables['points_zc'][:][points].copy())).T - cfdgrid['set'] = np.arange(6 * cfdgrid['n']).reshape(-1, 6) - cfdgrid['desc'] = str(marker) - cfdgrid['points_of_surface'] = [points_of_surface[s] for s in surfaces] - self.cfdgrids[str(marker)] = cfdgrid ncfile_grid.close() - def read_cfdmesh_su2(self, merge_domains=False): + def read_su2(self, filename_grid, markers=None): """ The description of the SU2 mesh file format is given here: https://su2code.github.io/docs/Mesh-File/ - - Splitting lines into parameters and values: According to the mesh specification (and like in all SU2 config files), - a '=' is always followed by a space, for example 'PARAMETER_XY= value'. Some mesh generators use a more relaxed - syntax like 'PARAMETER_XY=value', meaning that a line needs to be split at the '=' and not the space. """ - logging.info('Extracting points belonging to surface marker(s) from grid {}'.format(self.filename_grid)) + logging.info('Extracting points belonging to surface marker(s) from grid %s', filename_grid) # Open the ascii file and read all lines. - with open(self.filename_grid, 'r') as fid: + with open(filename_grid, 'r', encoding='utf-8') as fid: lines = fid.readlines() # Loop over all lines, if a keyword such as NELEM, NPOIN or MARKER_TAG is found, # this announces a new section in the file. surface_points = {} n_lines = len(lines) i = 0 + all_points = None while i < n_lines: + # Splitting lines into parameters and values: According to the mesh specification (and like in all SU2 config + # files), a '=' is always followed by a space, for example 'PARAMETER_XY= value'. Some mesh generators use a + # more relaxed syntax like 'PARAMETER_XY=value', meaning that a line needs to be split at the '=' and not the + # space. if str.find(lines[i], 'NELEM') != -1: - n_elem = int(lines[i].split('=')[1]) # There is nothing we need to do with the volume element connectivity, so we can skip this section. # Skipping all lines (at once) saves a lot of time, since there are many volume elements... + n_elem = int(lines[i].split('=')[1]) i += n_elem elif str.find(lines[i], 'NPOIN') != -1: - # New section found. # Here, the coordinates of all points are given, including surface and volume points. n_points = int(lines[i].split('=')[1]) i += 1 @@ -166,10 +116,9 @@ def read_cfdmesh_su2(self, merge_domains=False): tmp = [] for x in range(n_points): tmp.append(lines[i + x].split()) - points = np.array(tmp, dtype=float) + all_points = np.array(tmp, dtype=float) i += x elif str.find(lines[i], 'MARKER_TAG') != -1: - # New section found. # Here, all points are listed that belong to one marker. # In addition, the connectivity is given, which we need e.g. for plotting. marker = lines[i].split('=')[1].strip() @@ -188,44 +137,47 @@ def read_cfdmesh_su2(self, merge_domains=False): # Quadrilateral elements are identified with a 9 quadrilaterals.append([int(id) for id in split_line[1:]]) else: - logging.error('Surface elements of type "{}" are not implemented!'.format(split_line[0])) - + logging.error('Surface elements of type "%s" are not implemented!', split_line[0]) + points_on_surface = list(chain.from_iterable(triangles + quadrilaterals)) i += x # Store everything surface_points[marker] = {} - surface_points[marker]['points'] = np.unique(triangles + quadrilaterals) - surface_points[marker]['points_of_surface'] = triangles + quadrilaterals + surface_points[marker]['triangles'] = triangles + surface_points[marker]['quadrilaterals'] = quadrilaterals + surface_points[marker]['points_of_surface'] = np.unique(points_on_surface) i += 1 - if merge_domains: - # First we need to do some merging... - tmp_points = np.array([], dtype=int) - tmp_surfaces = [] - for marker in self.markers: - tmp_points = np.hstack((tmp_points, surface_points[marker]['points'])) - tmp_surfaces += surface_points[marker]['points_of_surface'] + # Assemble the cfdgrids, one grid for each marker + for marker, item in surface_points.items(): + cfdgrid = {} + cfdgrid['ID'] = item['points_of_surface'] + cfdgrid['CP'] = np.zeros(cfdgrid['ID'].shape) + cfdgrid['CD'] = np.zeros(cfdgrid['ID'].shape) + cfdgrid['n'] = len(cfdgrid['ID']) + cfdgrid['offset'] = all_points[cfdgrid['ID'], :3] + cfdgrid['set'] = np.arange(6 * cfdgrid['n']).reshape(-1, 6) + cfdgrid['desc'] = marker + cfdgrid['triangles'] = item['triangles'] + cfdgrid['quadrilaterals'] = item['quadrilaterals'] + self.cfdgrids[marker] = cfdgrid - # Assemble the cfdgrid - self.cfdgrid = {} - self.cfdgrid['ID'] = np.unique(tmp_points) - self.cfdgrid['CP'] = np.zeros(self.cfdgrid['ID'].shape) - self.cfdgrid['CD'] = np.zeros(self.cfdgrid['ID'].shape) - self.cfdgrid['n'] = len(self.cfdgrid['ID']) - self.cfdgrid['offset'] = points[self.cfdgrid['ID'], :3] - self.cfdgrid['set'] = np.arange(6 * self.cfdgrid['n']).reshape(-1, 6) - self.cfdgrid['desc'] = self.markers - self.cfdgrid['points_of_surface'] = tmp_surfaces - else: - self.cfdgrids = {} - for marker in surface_points.keys(): - # Assemble the cfdgrid - cfdgrid = {} - cfdgrid['ID'] = surface_points[marker]['points'] - cfdgrid['CP'] = np.zeros(cfdgrid['ID'].shape) - cfdgrid['CD'] = np.zeros(cfdgrid['ID'].shape) - cfdgrid['n'] = len(cfdgrid['ID']) - cfdgrid['offset'] = points[cfdgrid['ID'], :3] - cfdgrid['set'] = np.arange(6 * cfdgrid['n']).reshape(-1, 6) - cfdgrid['desc'] = marker - cfdgrid['points_of_surface'] = surface_points[marker]['points_of_surface'] - self.cfdgrids[marker] = cfdgrid + # Merge all desired markers into one cfdgrid + points_of_all_surfaces = [] + all_triangles = [] + all_quadrilaterals = [] + # If no markers are specified, all markers are merged into one cfdgrid. + if markers is None: + markers = surface_points.keys() + for marker in markers: + points_of_all_surfaces += surface_points[marker]['points_of_surface'].tolist() + all_triangles += surface_points[marker]['triangles'] + all_quadrilaterals += surface_points[marker]['quadrilaterals'] + self.cfdgrid['ID'] = np.unique(points_of_all_surfaces) + self.cfdgrid['CP'] = np.zeros(self.cfdgrid['ID'].shape) + self.cfdgrid['CD'] = np.zeros(self.cfdgrid['ID'].shape) + self.cfdgrid['n'] = len(self.cfdgrid['ID']) + self.cfdgrid['offset'] = all_points[self.cfdgrid['ID'], :3] + self.cfdgrid['set'] = np.arange(6 * self.cfdgrid['n']).reshape(-1, 6) + self.cfdgrid['desc'] = markers + self.cfdgrid['triangles'] = all_triangles + self.cfdgrid['quadrilaterals'] = all_quadrilaterals diff --git a/loadskernel/io_functions/read_mona.py b/loadskernel/io_functions/read_mona.py index 75c6cd8f..40e71857 100644 --- a/loadskernel/io_functions/read_mona.py +++ b/loadskernel/io_functions/read_mona.py @@ -177,12 +177,12 @@ def add_GRIDS(pandas_grids): # This functions relies on the Pandas data frames from the bdf reader. n = pandas_grids.shape[0] strcgrid = {} - strcgrid['ID'] = pandas_grids['ID'].to_numpy(dtype='int') - strcgrid['CD'] = pandas_grids['CD'].to_numpy(dtype='int') - strcgrid['CP'] = pandas_grids['CP'].to_numpy(dtype='int') + strcgrid['ID'] = pandas_grids['ID'].to_numpy(dtype='int').copy() + strcgrid['CD'] = pandas_grids['CD'].to_numpy(dtype='int').copy() + strcgrid['CP'] = pandas_grids['CP'].to_numpy(dtype='int').copy() strcgrid['n'] = n strcgrid['set'] = np.arange(n * 6).reshape((n, 6)) - strcgrid['offset'] = pandas_grids[['X1', 'X2', 'X3']].to_numpy(dtype='float') + strcgrid['offset'] = pandas_grids[['X1', 'X2', 'X3']].to_numpy(dtype='float').copy() return strcgrid @@ -190,7 +190,7 @@ def add_shell_elements(pandas_panels): # This functions relies on the Pandas data frames from the bdf reader. strcshell = {} n = pandas_panels.shape[0] - strcshell['ID'] = pandas_panels['ID'].to_numpy(dtype='int') + strcshell['ID'] = pandas_panels['ID'].to_numpy(dtype='int').copy() strcshell['cornerpoints'] = np.array(pandas_panels[['G1', 'G2', 'G3', 'G4']]) strcshell['CD'] = np.zeros(n) # Assumption: panels are given in global coord system strcshell['CP'] = np.zeros(n) @@ -207,8 +207,8 @@ def add_panels_from_CAERO(pandas_caero, pandas_aefact): panels = {"ID": [], 'CP': [], 'CD': [], "cornerpoints": []} for index, caerocard in pandas_caero.iterrows(): # get the four corner points of the CAERO card - X1 = caerocard[['X1', 'Y1', 'Z1']].to_numpy(dtype='float') - X4 = caerocard[['X4', 'Y4', 'Z4']].to_numpy(dtype='float') + X1 = caerocard[['X1', 'Y1', 'Z1']].to_numpy(dtype='float').copy() + X4 = caerocard[['X4', 'Y4', 'Z4']].to_numpy(dtype='float').copy() X2 = X1 + np.array([caerocard['X12'], 0.0, 0.0]) X3 = X4 + np.array([caerocard['X43'], 0.0, 0.0]) # calculate LE, Root and Tip vectors [x,y,z]^T @@ -362,9 +362,9 @@ def add_CORD2R(pandas_cord2r, coord): for _, row in pandas_cord2r.iterrows(): ID = int(row['ID']) RID = int(row['RID']) - A = row[['A1', 'A2', 'A3']].to_numpy(dtype='float').squeeze() - B = row[['B1', 'B2', 'B3']].to_numpy(dtype='float').squeeze() - C = row[['C1', 'C2', 'C3']].to_numpy(dtype='float').squeeze() + A = row[['A1', 'A2', 'A3']].to_numpy(dtype='float').squeeze().copy() + B = row[['B1', 'B2', 'B3']].to_numpy(dtype='float').squeeze().copy() + C = row[['C1', 'C2', 'C3']].to_numpy(dtype='float').squeeze().copy() # build coord z = B - A y = np.cross(B - A, C - A) @@ -466,9 +466,9 @@ def add_MONPNT1(pandas_monpnts): mongrid['ID'] = np.arange(1, n + 1) mongrid['name'] = pandas_monpnts['NAME'].to_list() mongrid['comp'] = pandas_monpnts['COMP'].to_list() - mongrid['CD'] = pandas_monpnts['CD'].to_numpy(dtype='int') - mongrid['CP'] = pandas_monpnts['CP'].to_numpy(dtype='int') + mongrid['CD'] = pandas_monpnts['CD'].to_numpy(dtype='int').copy() + mongrid['CP'] = pandas_monpnts['CP'].to_numpy(dtype='int').copy() mongrid['n'] = n mongrid['set'] = np.arange(n * 6).reshape((n, 6)) - mongrid['offset'] = pandas_monpnts[['X', 'Y', 'Z']].to_numpy(dtype='float') + mongrid['offset'] = pandas_monpnts[['X', 'Y', 'Z']].to_numpy(dtype='float').copy() return mongrid diff --git a/loadskernel/io_functions/read_op2.py b/loadskernel/io_functions/read_op2.py index f83f2de0..d57e8fa0 100644 --- a/loadskernel/io_functions/read_op2.py +++ b/loadskernel/io_functions/read_op2.py @@ -34,6 +34,7 @@ import sys import struct +import logging import numpy as np # Notes on the op2 format. @@ -200,7 +201,7 @@ def _op2_open(self, filename): self._int32stru = self._endian + '%di' self._read_op2_header() self._postheaderpos = self._fileh.tell() - self.directory(verbose=True) + self.directory(verbose=False) def _get_key(self): """Reads [reclen, key, endrec] triplet and returns key.""" @@ -309,6 +310,54 @@ def _read_op2_name_trailer(self): rec_type = self._get_key() return db_name, trailer, rec_type + def read_op2_matrix(self, name, trailer): + """ + Read and return Nastran op2 matrix at current file position. + + It is assumed that the name has already been read in via + :func:`_read_op2_name_trailer`. + + The size of the matrix is read from trailer: + nrows = trailer[2] + ncols = trailer[1] + """ + dtype = 1 + nrows = trailer[2] + ncols = trailer[1] + # print(' %s (%s, %s)' % (name, nrows, ncols)) + matrix = np.zeros((nrows, ncols), order='F') + if self._bit64: + intsize = 8 + else: + intsize = 4 + col = 0 + frm = self._endian + '%dd' + # print('frm =', frm) + while dtype > 0: # read in matrix columns + # key is number of elements in next record (row # followed + # by key-1 real numbers) + key = self._get_key() + # read column + while key > 0: + reclen = self._Str4.unpack(self._fileh.read(4))[0] + r = self._Str.unpack(self._fileh.read(self._ibytes))[0] - 1 + n = (reclen - intsize) // 8 + if n < self._rowsCutoff: + matrix[r:r + n, col] = struct.unpack( + frm % n, self._fileh.read(n * 8)) + else: + matrix[r:r + n, col] = np.fromfile( + self._fileh, np.float64, n) + self._fileh.read(4) # endrec + key = self._get_key() + col += 1 + self._get_key() + dtype = self._get_key() + self._read_op2_end_of_table() + if self._swap: + matrix = matrix.byteswap() + return matrix + def skip_op2_matrix(self, trailer): """ Skip Nastran op2 matrix at current position. @@ -535,7 +584,7 @@ def skip_op2_record(self): key = self._get_key() self._skip_key(2) - def _read_op2_uset(self): + def read_op2_uset(self): """ Read the USET data block. @@ -550,51 +599,41 @@ def _read_op2_uset(self): if any(sset): uset[sset] = uset[sset] & ~2 self._read_op2_end_of_table() + # We don't know why, but the USET exported from Nastran 95 is not zeros and ones but 17 and another large number + # (e.g. 496 or 1074, possibly depending on the operating system). + if max(uset) > 3: + logging.info("USET from Nastran 95 detected, attempt conversion of data to binary") + uset[uset < 20] = 1 + uset[uset > 20] = 2 return uset -def read_post_op2(op2_filename, verbose=False): +def read_op2(op2_filename): """ - Reads PARAM,POST,-1 op2 file and returns dictionary of data. - - Parameters - ---------- - op2_filename : string - Name of op2 file. - verbose : bool - If true, echo names of tables and matrices to screen - - Returns dictionary with following members - ----------------------------------------- - 'uset' : array + Reads op2 file and returns dictionary of data. """ - # read op2 file: + # Open op2 file with OP2(op2_filename) as o2: - uset = None + data = {} o2._fileh.seek(o2._postheaderpos) while 1: name, trailer, dbtype = o2._read_op2_name_trailer() - # print('name = %r' % name) - # print('trailer = %s' % str(trailer)) - # print('dbtype = %r' % dbtype) + # Condition to stop iterating if name is None: break + # Catch error condition if name == '': - raise RuntimeError('name=%r' % name) + raise RuntimeError('name={name}') + # Read matrices, typically stiffness and mass matrices such as Kgg, Mgg and GM if dbtype > 0: - if verbose: - print("Skipping matrix {0}...".format(name)) - o2.skip_op2_matrix(trailer) + logging.debug("Reading matrix %s...", name) + data[name] = o2.read_op2_matrix(name, trailer) + # Read USET table but skip other tables + elif name.find('USET') == 0: + logging.debug("Reading table %s...", name) + data['uset'] = o2.read_op2_uset() else: - if name.find('USET') == 0: - if verbose: - print("Reading USET table {0}...".format(name)) - uset = o2._read_op2_uset() - continue - else: - if verbose: - print("Skipping table %r..." % name) + logging.debug("Skipping table %s...", name) o2.skip_op2_table() - - return {'uset': uset} + return data diff --git a/loadskernel/model.py b/loadskernel/model.py index 52f8a568..67659995 100644 --- a/loadskernel/model.py +++ b/loadskernel/model.py @@ -9,7 +9,8 @@ from panelaero import VLM, DLM -from loadskernel.fem_interfaces import nastran_interface, nastran_f06_interface, cofe_interface, b2000_interface +from loadskernel.fem_interfaces import (nastran_interface, nastran_f06_interface, cofe_interface, b2000_interface, + nastran95_interface) from loadskernel import build_aero_functions from loadskernel import spline_rules from loadskernel import spline_functions @@ -62,7 +63,7 @@ def build_coord(self): def build_strc(self): logging.info('Building structural model...') - if self.jcl.geom['method'] == 'mona': + if self.jcl.geom['method'] in ['mona']: # parse given bdf files self.bdf_reader.process_deck(self.jcl.geom['filename_grid']) # assemble strcgrid, sort grids to be in accordance with matricies such as Mgg from Nastran @@ -106,7 +107,7 @@ def build_strcshell(self): self.bdf_reader.cards['CTRIA3']], ignore_index=True)) def build_mongrid(self): - if self.jcl.geom['method'] in ['mona', 'CoFE']: + if self.jcl.geom['method'] in ['mona']: if 'filename_mongrid' in self.jcl.geom and not self.jcl.geom['filename_mongrid'] == '': logging.info('Building Monitoring Stations from GRID data...') self.mongrid = read_mona.Modgen_GRID(self.jcl.geom['filename_mongrid']) @@ -137,8 +138,8 @@ def build_mongrid(self): logging.warning('No Monitoring Stations are created!') """ This is an empty dummy monitoring stations, which is necessary when no monitoring stations are defined, - because monstations are expected to exist for example for the calculation of cutting forces, which are in - turn expected in the post processing. However, this procedure allows the code to run without any given + because monstations are expected to exist for example for the calculation of internal section forces, which + are in turn expected in the recovery step. However, this procedure allows the code to run without any given monitoring stations, which are not available for all models. """ self.mongrid = {'ID': np.array([0]), @@ -502,13 +503,12 @@ def build_cfdgrid(self): # ---- mesh defo --- # ------------------- if self.jcl.aero['method'] in ['cfd_steady', 'cfd_unsteady']: - cfdgrids = read_cfdgrids.ReadCfdgrids(self.jcl) - cfdgrids.read_surface(merge_domains=True) - cfdgrids.read_surface(merge_domains=False) + cfdgrids = read_cfdgrids.ReadCfdgrids() + cfdgrids.read_surface(self.jcl) self.cfdgrid = cfdgrids.cfdgrid self.cfdgrids = cfdgrids.cfdgrids - logging.info('The CFD surface grid consists of {} grid points and {} boundary markers.'.format( - self.cfdgrid['n'], self.cfdgrids.__len__())) + logging.info('The CFD surface grid consists of %s grid points and %s boundary markers.', + self.cfdgrid['n'], len(self.cfdgrids)) # Option A: CFD forces are transferred to the aerogrid. # This allows a direct integration into existing procedures and a comparison to VLM forces. @@ -531,7 +531,7 @@ def build_cfdgrid(self): def build_structural_dynamics(self): logging.info('Building stiffness and mass model...') self.mass = {} - if self.jcl.mass['method'] in ['mona', 'f06', 'modalanalysis', 'guyan', 'CoFE', 'B2000']: + if self.jcl.mass['method'] in ['mona', 'f06', 'modalanalysis', 'guyan', 'CoFE', 'B2000', 'Nastran95']: # select the fem interface if self.jcl.mass['method'] in ['modalanalysis', 'guyan']: @@ -542,6 +542,8 @@ def build_structural_dynamics(self): fem_interface = b2000_interface.B2000Interface(self.jcl, self.strcgrid, self.coord) elif self.jcl.mass['method'] in ['CoFE']: fem_interface = cofe_interface.CoFEInterface(self.jcl, self.strcgrid, self.coord) + elif self.jcl.mass['method'] in ['Nastran95']: + fem_interface = nastran95_interface.Nastran95Interface(self.jcl, self.strcgrid, self.coord) # the stiffness matrix is needed for all methods / fem interfaces fem_interface.get_stiffness_matrix() @@ -549,10 +551,14 @@ def build_structural_dynamics(self): self.KGG = fem_interface.KGG # Check if matrix is symmetric if not fem_helper.check_matrix_symmetry(self.KGG): - logging.warning('Stiffness matrix Kgg is NOT symmetric.') + if fem_helper.check_matrix_symmetry_allclose(self.KGG): + logging.warning('Stiffness matrix Kgg is only symmetric within numerical tolerances.') + else: + logging.warning('Stiffness matrix Kgg is NOT symmetric. \ + This may lead to problems in the modal analysis and should be checked carefully.') # do further processing of the stiffness matrix - if self.jcl.mass['method'] in ['modalanalysis', 'guyan', 'CoFE', 'B2000']: + if self.jcl.mass['method'] in ['modalanalysis', 'guyan', 'CoFE', 'B2000', 'Nastran95']: fem_interface.get_dofs() fem_interface.prepare_stiffness_matrices() if self.jcl.mass['method'] in ['guyan']: @@ -574,13 +580,16 @@ def build_mass_matrices(self, fem_interface, i_mass): MGG = fem_interface.get_mass_matrix(i_mass) # Check if matrix is symmetric if not fem_helper.check_matrix_symmetry(MGG): - logging.warning('Mass matrix Mgg is NOT symmetric.') - + if fem_helper.check_matrix_symmetry_allclose(MGG): + logging.warning('Mass matrix Mgg is only symmetric within numerical tolerances.') + else: + logging.warning('Mass matrix Mgg is NOT symmetric. \ + This may lead to problems in the modal analysis and should be checked carefully.') # getting the eigenvalues and -vectors depends on the method / fem solver - if self.jcl.mass['method'] in ['modalanalysis', 'guyan', 'CoFE', 'B2000']: + if self.jcl.mass['method'] in ['modalanalysis', 'guyan', 'CoFE', 'B2000', 'Nastran95']: Mb, cggrid, cggrid_norm = fem_interface.calc_cg() fem_interface.prepare_mass_matrices() - if self.jcl.mass['method'] in ['modalanalysis', 'CoFE', 'B2000']: + if self.jcl.mass['method'] in ['modalanalysis', 'CoFE', 'B2000', 'Nastran95']: fem_interface.modalanalysis() elif self.jcl.mass['method'] in ['guyan']: fem_interface.guyanreduction() diff --git a/loadskernel/plotting_extra.py b/loadskernel/plotting_extra.py index d3d274d0..0904820e 100755 --- a/loadskernel/plotting_extra.py +++ b/loadskernel/plotting_extra.py @@ -12,8 +12,8 @@ pass from loadskernel import plotting_standard -from modelviewer import plotting as plotting_modelviewer from loadskernel.io_functions.data_handling import load_hdf5_dict +from modelviewer import plotting as plotting_modelviewer plt.rcParams.update({'font.size': 16, 'svg.fonttype': 'none'}) @@ -46,146 +46,6 @@ def plot_pressure_distribution(self): self.set_view_left_above() mlab.show() - def plot_time_data(self): - # Create all plots - _, (ax11, ax12) = plt.subplots(nrows=2, ncols=1, sharex=True,) - _, (ax21, ax22, ax23) = plt.subplots(nrows=3, ncols=1, sharex=True,) - _, (ax31, ax32) = plt.subplots(nrows=2, ncols=1, sharex=True,) - _, (ax41, ax42) = plt.subplots(nrows=2, ncols=1, sharex=True,) - _, (ax51, ax52, ax53) = plt.subplots(nrows=3, ncols=1, sharex=True,) - _, (ax61, ax62) = plt.subplots(nrows=2, ncols=1, sharex=True,) - if hasattr(self.jcl, 'landinggear'): - _, (ax71, ax72) = plt.subplots(nrows=2, ncols=1, sharex=True,) - Dkx1 = self.model['Dkx1'][()] - # Loop over responses and fill plots with data - for response in self.responses: - trimcase = self.jcl.trimcase[response['i'][()]] - logging.info('plotting for simulation {:s}'.format(trimcase['desc'])) - - self.n_modes = self.model['mass'][trimcase['mass']]['n_modes'][()] - - if self.jcl.aero['method'] in ['mona_steady', 'mona_unsteady', 'nonlin_steady']: - Cl = response['Pmac'][:, 2] / response['q_dyn'][:].T / self.jcl.general['A_ref'] - ax11.plot(response['t'], response['Pmac'][:, 2], 'b-') - ax12.plot(response['t'], Cl.T, 'b-') - - ax21.plot(response['t'], response['q_dyn'], 'k-') - ax22.plot(response['t'], response['alpha'][:] / np.pi * 180.0, 'r-') - ax22.plot(response['t'], response['beta'][:] / np.pi * 180.0, 'c-') - ax23.plot(response['t'], response['Nxyz'][:, 1], 'g-') - ax23.plot(response['t'], response['Nxyz'][:, 2], 'b-') - - if self.jcl.aero['method'] in ['mona_unsteady']: - Pb_gust = [] - Pb_unsteady = [] - for i_step in range(len(response['t'])): - Pb_gust.append(np.dot(Dkx1.T, response['Pk_gust'][i_step, :])[2]) - Pb_unsteady.append(np.dot(Dkx1.T, response['Pk_unsteady'][i_step, :])[2]) - ax11.plot(response['t'], Pb_gust, 'k-') - ax11.plot(response['t'], Pb_unsteady, 'r-') - - ax31.plot(response['t'], response['X'][:, 0], 'b-') - ax31.plot(response['t'], response['X'][:, 1], 'g-') - ax31.plot(response['t'], response['X'][:, 2], 'r-') - - ax32.plot(response['t'], response['X'][:, 3] / np.pi * 180.0, 'b-') - ax32.plot(response['t'], response['X'][:, 4] / np.pi * 180.0, 'g-') - ax32.plot(response['t'], response['X'][:, 5] / np.pi * 180.0, 'r-') - - ax41.plot(response['t'], response['X'][:, 6], 'b-') - ax41.plot(response['t'], response['X'][:, 7], 'g-') - ax41.plot(response['t'], response['X'][:, 8], 'r-') - - ax42.plot(response['t'], response['X'][:, 9] / np.pi * 180.0, 'b-') - ax42.plot(response['t'], response['X'][:, 10] / np.pi * 180.0, 'g-') - ax42.plot(response['t'], response['X'][:, 11] / np.pi * 180.0, 'r-') - - ax51.plot(response['t'], response['X'][:, 12 + 2 * self.n_modes + 0] / np.pi * 180.0, 'b-') - ax51.plot(response['t'], response['X'][:, 12 + 2 * self.n_modes + 1] / np.pi * 180.0, 'g-') - ax51.plot(response['t'], response['X'][:, 12 + 2 * self.n_modes + 2] / np.pi * 180.0, 'r-') - - ax52.plot(response['t'], response['X'][:, 12 + 2 * self.n_modes + 3], 'k-') - - ax53.plot(response['t'], response['X'][:, 12 + 2 * self.n_modes + 4], 'b-') - ax53.plot(response['t'], response['X'][:, 12 + 2 * self.n_modes + 5], 'g-') - - ax61.plot(response['t'], response['Uf'], 'b-') - - ax62.plot(response['t'], response['d2Ucg_dt2'][:, 0], 'b-') - ax62.plot(response['t'], response['d2Ucg_dt2'][:, 1], 'g-') - ax62.plot(response['t'], response['d2Ucg_dt2'][:, 2], 'r-') - - if hasattr(self.jcl, 'landinggear'): - ax71.plot(response['t'], response['p1']) - ax72.plot(response['t'], response['F1']) - - # Make plots nice - ax11.set_ylabel('Fz [N]') - ax11.grid(True) - if self.jcl.aero['method'] in ['mona_unsteady']: - ax11.legend(['aero', 'gust', 'unsteady']) - ax12.set_xlabel('t [sec]') - ax12.set_ylabel('Cz [-]') - ax12.grid(True) - ax12.legend(['Cz']) - - ax21.set_ylabel('[Pa]') - ax21.grid(True) - ax21.legend(['q_dyn']) - ax22.legend(['alpha', 'beta']) - ax22.grid(True) - ax22.set_ylabel('[deg]') - ax23.set_xlabel('t [sec]') - ax23.legend(['Ny', 'Nz']) - ax23.grid(True) - ax23.set_ylabel('[-]') - - ax31.set_ylabel('[m]') - ax31.grid(True) - ax31.legend(['x', 'y', 'z']) - ax32.set_xlabel('t [sec]') - ax32.set_ylabel('[deg]') - ax32.grid(True) - ax32.legend(['phi', 'theta', 'psi']) - - ax41.set_ylabel('[m/s]') - ax41.grid(True) - ax41.legend(['u', 'v', 'w']) - ax42.set_xlabel('t [sec]') - ax42.set_ylabel('[deg/s]') - ax42.grid(True) - ax42.legend(['p', 'q', 'r']) - - ax51.set_ylabel('Inputs [deg]') - ax51.grid(True) - ax51.legend(['Xi', 'Eta', 'Zeta']) - ax52.set_ylabel('Inputs [N]') - ax52.grid(True) - ax52.legend(['Thrust']) - ax53.set_xlabel('t [sec]') - ax53.set_ylabel('Inputs [deg]') - ax53.grid(True) - ax53.legend(['stabilizer', 'flap setting']) - - ax61.set_ylabel('Uf') - ax61.grid(True) - ax62.set_xlabel('t [sec]') - ax62.set_ylabel('d2Ucg_dt2 [m/s^2]') - ax62.legend(['du', 'dv', 'dw']) - ax62.grid(True) - - if hasattr(self.jcl, 'landinggear'): - ax71.legend(self.jcl.landinggear['key'], loc='best') - ax71.set_ylabel('p1 [m]') - ax71.grid(True) - ax72.legend(self.jcl.landinggear['key'], loc='best') - ax72.set_xlabel('t [s]') - ax72.set_ylabel('F1 [N]') - ax72.grid(True) - - # Show plots - plt.show() - def plot_forces_deformation_interactive(self): # loop over all responses diff --git a/loadskernel/plotting_standard.py b/loadskernel/plotting_standard.py index 49d622a8..ba62b027 100755 --- a/loadskernel/plotting_standard.py +++ b/loadskernel/plotting_standard.py @@ -77,9 +77,9 @@ def plot_monstations(self, filename_pdf): self.pp = PdfPages(filename_pdf) self.potato_plots() if self.cuttingforces_wing: - self.cuttingforces_along_axis_plots(monstations=self.cuttingforces_wing, axis=1) + self.plot_loads_along_axis(monstations=self.cuttingforces_wing, axis=1) if self.cuttingforces_fuselage: - self.cuttingforces_along_axis_plots(monstations=self.cuttingforces_fuselage, axis=0) + self.plot_loads_along_axis(monstations=self.cuttingforces_fuselage, axis=0) self.pp.close() logging.info('Plots saved as %s', filename_pdf) @@ -196,9 +196,9 @@ def potato_plots(self): self.potato_plot_nicely(station, station, dof_xaxis, dof_yaxis, var_xaxis, var_yaxis) plt.close() - def cuttingforces_along_axis_plots(self, monstations, axis): + def plot_loads_along_axis(self, monstations, axis): assert axis in [0, 1, 2], 'Plotting along an axis only supported for axis 0, 1 or 2!' - logging.info('Start plotting cutting forces along axis %d...', axis) + logging.info('Start plotting internal section loads along axis %d...', axis) # Read the data required for plotting. loads = [] offsets = [] @@ -273,7 +273,7 @@ def cuttingforces_along_axis_plots(self, monstations, axis): plt.close() def plot_monstations_time(self, filename_pdf): - logging.info('start plotting cutting forces over time ...') + logging.info('start plotting internal section loads over time ...') pp = PdfPages(filename_pdf) potato = np.sort(np.unique(self.potatos_fz_mx + self.potatos_mx_my + self.potatos_fz_my + self.potatos_fy_mx + self.potatos_mx_mz + self.potatos_my_mz)) diff --git a/loadskernel/program_flow.py b/loadskernel/program_flow.py index 2e83c6c8..7a916837 100755 --- a/loadskernel/program_flow.py +++ b/loadskernel/program_flow.py @@ -14,7 +14,7 @@ pass import loadskernel -from loadskernel import solution_sequences, post_processing, gather_loads, auxiliary_output, plotting_standard +from loadskernel import recover_loads_and_defo, solution_sequences, gather_loads, auxiliary_output, plotting_standard from loadskernel.io_functions import data_handling import loadskernel.model as model_modul from loadskernel.cfd_interfaces.mpi_helper import setup_mpi @@ -75,56 +75,48 @@ def print_logo(self): def setup_logger_cluster(self, i): # Generate a separate filename for each subcase path_log = data_handling.check_path(self.path_output + 'log/') - filename = path_log + 'log_' + self.job_name + '_subcase_' + str(self.jcl.trimcase[i]['subcase']) \ - + '.txt.' + str(self.myid) + filename = path_log + f'log_{self.job_name}_subcase_{self.jcl.trimcase[i]['subcase']}.txt.{self.myid}' # Then create the logger and console output self.create_logfile_and_console_output(filename) def setup_logger(self): # Generate a generic name for the log file path_log = data_handling.check_path(self.path_output + 'log/') - filename = path_log + 'log_' + self.job_name + '.txt.' + str(self.myid) + filename = path_log + f'log_{self.job_name}.txt.{self.myid}' # Then create the logger and console output self.create_logfile_and_console_output(filename) def create_logfile_and_console_output(self, filename): logger = logging.getLogger() + # Disable propagation to avoid duplicate outputs in case of multiple handlers (e.g. console and file handler). + logger.propagate = False + # Clear previous handlers. + if logger.hasHandlers(): + logger.handlers.clear() # Set logging level. if self.debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) - # Get the names of all existing loggers. - existing_handlers = [hdlr.get_name() for hdlr in logger.handlers] - if 'lk_logfile' in existing_handlers: - # Make sure that the filename is still correct. - hdlr = logger.handlers[existing_handlers.index('lk_logfile')] - if not hdlr.baseFilename == filename: - # In case the filename is incorrect, remove the handler completely from the logger. - logger.removeHandler(hdlr) - # Update the list of all existing loggers. - existing_handlers = [hdlr.get_name() for hdlr in logger.handlers] - - # Add the following handlers only if they don't exist. This avoid duplicate lines/log entries. - if 'lk_logfile' not in existing_handlers: - # define a Handler which writes messages to a log file - logfile = logging.FileHandler(filename, mode='a') - logfile.set_name('lk_logfile') - formatter = logging.Formatter(fmt='%(asctime)s %(processName)-14s %(levelname)s: %(message)s', - datefmt='%d/%m/%Y %H:%M:%S') - logfile.setFormatter(formatter) - logger.addHandler(logfile) - + # Define a Handler which writes messages to a log file + logfile = logging.FileHandler(filename, mode='a') + logfile.set_name('lk_logfile') + logfile.propagate = False + formatter = logging.Formatter(fmt='%(asctime)s %(processName)-14s %(levelname)s: %(message)s', + datefmt='%d/%m/%Y %H:%M:%S') + logfile.setFormatter(formatter) + # Add the handler to the root logger + logger.addHandler(logfile) # For convinience, the first rank writes console outputs, too. - if (self.myid == 0) and ('lk_console' not in existing_handlers): - # define a Handler which writes messages to the sys.stout + if self.myid == 0: + # Define a Handler which writes messages to the sys.stout console = logging.StreamHandler(sys.stdout) console.set_name('lk_console') - # set a format which is simpler for console use + console.propagate = False + # Set a format which is simpler for console use and tell the handler to use this format formatter = logging.Formatter(fmt='%(levelname)s: %(message)s') - # tell the handler to use this format console.setFormatter(formatter) - # add the handler(s) to the root logger + # Add the handler to the root logger logger.addHandler(console) logger.info('This is the log for process %s.', self.myid) @@ -197,10 +189,11 @@ def main_core(self, model, jcl, i): # Also, the name 'post_processing' might be misleading here, as it is not post processing of the entire # job (post=True), but only of the trim / sim solution sequence. if solution_i.successful: - post_processing_i = post_processing.PostProcessing(jcl, model, jcl.trimcase[i], solution_i.response) + post_processing_i = recover_loads_and_defo.RecoverLoadsAndDeformations(jcl, model, jcl.trimcase[i], + solution_i.response) post_processing_i.force_summation_method() post_processing_i.euler_transformation() - post_processing_i.cuttingforces() + post_processing_i.integrate_loads() del post_processing_i # Look if any other special analyses are requested (such as flutter, derivatives, pulses) in the simcase. if 'flutter' in jcl.simcase[i] and jcl.simcase[i]['flutter']: @@ -212,8 +205,8 @@ def main_core(self, model, jcl, i): solution_i.exec_pulse() # Collect response from solution sequence, then destroy it to free memory. response = solution_i.response - response['i'] = i response['successful'] = solution_i.successful + response['i'] = i del solution_i return response @@ -409,14 +402,6 @@ def run_test(self): logging.info('Drawing some more detailed plots.') plt = plotting_extra.DetailedPlots(self.jcl, model) plt.add_responses(responses) - if 't_final' and 'dt' in self.jcl.simcase[0].keys(): - # show some plots of the time domain data - plt.plot_time_data() - else: - # show some plots of the force vectors, useful to identify model shortcomings - # plt.plot_pressure_distribution() - plt.plot_forces_deformation_interactive() - if 't_final' and 'dt' in self.jcl.simcase[0].keys(): # show a nice animation of the time domain simulation plt = plotting_extra.Animations(self.jcl, model) @@ -424,6 +409,10 @@ def run_test(self): plt.make_animation() # make a video file of the animation # plt.make_movie(self.path_output, speedup_factor=1.0) + else: + # show some plots of the force vectors, useful to identify model shortcomings + # plt.plot_pressure_distribution() + plt.plot_forces_deformation_interactive() """ At the moment, I also use this section for custom analysis scripts. diff --git a/loadskernel/post_processing.py b/loadskernel/recover_loads_and_defo.py similarity index 98% rename from loadskernel/post_processing.py rename to loadskernel/recover_loads_and_defo.py index e8fc54d9..18d639c0 100755 --- a/loadskernel/post_processing.py +++ b/loadskernel/recover_loads_and_defo.py @@ -7,7 +7,7 @@ from loadskernel.io_functions.data_handling import load_hdf5_sparse_matrix, load_hdf5_dict -class PostProcessing(): +class RecoverLoadsAndDeformations(): """ In this class calculations are made that follow up on every simulation. The functions should be able to handle both trim calculations and time simulations. @@ -153,8 +153,8 @@ def euler_transformation(self): dest_coord=1000000) response['Ug'][i_step, :] = response['Ug_r'][i_step, :] + response['Ug_f'][i_step, :] - def cuttingforces(self): - logging.info('Calculating cutting forces & moments...') + def integrate_loads(self): + logging.info('Calculating section forces & moments...') response = self.response response['Pmon_local'] = np.zeros((len(response['t']), 6 * self.mongrid['n'])) for i_step in range(len(response['t'])): diff --git a/loadskernel/spline_functions.py b/loadskernel/spline_functions.py index 2e2b5bbd..095a4951 100644 --- a/loadskernel/spline_functions.py +++ b/loadskernel/spline_functions.py @@ -104,7 +104,6 @@ class SplineRadialBasisFunctions: def __init__(self, nodes_fe, nodes_cfd, rbf_type, surface_spline, support_radius): self.surface_spline = surface_spline self.rbf_type = rbf_type - self.rbf_type = 'wendland2' self.R = support_radius if self.surface_spline: logging.debug('Using surface formulation (2D xy surface)') diff --git a/loadskernel/units.py b/loadskernel/units.py index c1e45b6e..b1b21106 100644 --- a/loadskernel/units.py +++ b/loadskernel/units.py @@ -75,3 +75,40 @@ def tas2Ma(tas, h): def eas2Ma(eas, h): tas = eas2tas(eas, h) return tas2Ma(tas, h) + + +def reynolds_number(rho, v, l_ref, T): + # Calculate Reynolds number, https://en.wikipedia.org/wiki/Reynolds_number + # Inputs: + # - rho: fluid density [kg/m^3] + # - v: fluid velocity [m/s] + # - l_ref: reference length [m] + # - T: temperature [K] + # + # Output: + # - Re: Reynolds number (dimensionless) + + mu = dynamic_viscosity_of_air(T) + Re = (rho * v * l_ref) / mu + return Re + + +def dynamic_viscosity_of_air(T): + # Calculate dynamic viscosity of air according to Sutherland's formula. + # The constants used here are from https://www.cfd-online.com/Wiki/Sutherland%27s_law and in line with the + # values used in SU2. Different sources (e.g. https://de.wikipedia.org/wiki/Sutherland-Modell) give slightly + # different values. + # Inputs: + # - T: temperature [K] + # + # Output: + # - mu: dynamic viscosity [Pa*s = kg/(m*s)] + + # reference viscosity at T_ref [kg/(m*s)] + mu_ref = 1.716e-5 + # reference temperature [K] + T_ref = 273.15 + # Sutherland's constant for air [K] + S = 110.4 + mu = mu_ref * (T_ref + S) / (T + S) * (T / T_ref) ** (3 / 2) + return mu diff --git a/modelviewer/cfdgrid.py b/modelviewer/cfdgrid.py index 64232335..165405e2 100644 --- a/modelviewer/cfdgrid.py +++ b/modelviewer/cfdgrid.py @@ -4,24 +4,17 @@ class TauGrid(loadskernel.io_functions.read_cfdgrids.ReadCfdgrids): - def __init__(self): - pass - def load_file(self, filename): self.filename_grid = filename self.get_markers() - self.read_cfdmesh_netcdf() + self.read_netcdf(self.filename_grid, self.markers) def get_markers(self): - ncfile_grid = netcdf.NetCDFFile(self.filename_grid, 'r') + ncfile_grid = netcdf.netcdf_file(self.filename_grid, 'r') self.markers = ncfile_grid.variables['marker'][:].tolist() class SU2Grid(loadskernel.io_functions.read_cfdgrids.ReadCfdgrids): - def __init__(self): - pass - def load_file(self, filename): - self.filename_grid = filename - self.read_cfdmesh_su2() + self.read_su2(filename) diff --git a/modelviewer/plotting.py b/modelviewer/plotting.py index c33189a4..fb7d8a4b 100644 --- a/modelviewer/plotting.py +++ b/modelviewer/plotting.py @@ -266,8 +266,7 @@ def plot_cfdgrids(self, markers): self.src_cfdgrids = [] for marker in self.cfdgrids: if marker in markers: - self.setup_cfdgrid_display( - grid=self.cfdgrids[marker], color=(1, 1, 1), scalars=None) + self.setup_cfdgrid_display(grid=self.cfdgrids[marker], color=(1, 1, 1), scalars=None) self.show_cfdgrids = True mlab.draw(self.fig) @@ -275,7 +274,7 @@ def setup_cfdgrid_display(self, grid, color, scalars): ug = tvtk.UnstructuredGrid(points=grid['offset']) # ug.point_data.scalars = scalars shells = [] - for shell in grid['points_of_surface']: + for shell in grid['triangles'] + grid['quadrilaterals']: shells.append([np.where(grid['ID'] == id)[0][0] for id in shell]) shell_type = tvtk.Polygon().cell_type ug.set_cells(shell_type, shells) diff --git a/modelviewer/view.py b/modelviewer/view.py index 77763da9..57f4f33b 100644 --- a/modelviewer/view.py +++ b/modelviewer/view.py @@ -497,13 +497,12 @@ def get_mass_data_for_plotting(self): Mb = self.model['mass'][key]['Mb'][()] cggrid = load_hdf5_dict(self.model['mass'][key]['cggrid']) self.plotting.plot_masses(Mgg, Mb, cggrid, rho) - self.lb_cg.setText(f"CG: x={cggrid['offset'][0, 0]:0.4f}, \ - y={cggrid['offset'][0, 1]:0.4f}, \ - z={cggrid['offset'][0, 2]:0.4f} m") + self.lb_cg.setText(f"CG: x={cggrid['offset'][0, 0]:0.4f}," + f"y={cggrid['offset'][0, 1]:0.4f}," + f"z={cggrid['offset'][0, 2]:0.4f} m") # cg_mac = (x_cg - x_mac)*c_ref * 100 [%] # negativ bedeutet Vorlage --> stabil - cg_mac = (cggrid['offset'][0, 0] - self.MAC[0]) / \ - self.model['macgrid']['c_ref'][()] * 100.0 + cg_mac = (cggrid['offset'][0, 0] - self.MAC[0]) / self.model['macgrid']['c_ref'][()] * 100.0 if cg_mac < 0.0: rating = 'stable' elif cg_mac > 0.0: diff --git a/responseviewer/__init__.py b/responseviewer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/responseviewer/plotting.py b/responseviewer/plotting.py new file mode 100644 index 00000000..63179098 --- /dev/null +++ b/responseviewer/plotting.py @@ -0,0 +1,85 @@ +import os +import matplotlib.pyplot as plt +import numpy as np + + +class Plotting(): + + states_avail = ['x [m]', 'y [m]', 'z [m]', 'Phi [deg]', 'Theta [deg]', 'Psi [deg]', + 'u [m/s]', 'v [m/s]', 'w [m/s]', 'p [deg/s]', 'q [deg/s]', 'r [deg/s]'] + commands_avail = ['Xi [deg]', 'Eta [deg]', 'Zeta [deg]', 'Thrust [N]', 'Stabilizer [deg]', 'Flaps [deg]'] + loadfactors_avail = ['Nx [-]', 'Ny [-]', 'Nz [-]'] + other_avail = ['q_dyn [Pa]', 'alpha [deg]', 'beta [deg]', 'p1 [m]', 'F1 [N]'] + all_quantities = states_avail + commands_avail + loadfactors_avail + other_avail + + def __init__(self, fig): + plt.rcParams.update({'font.size': 16, + 'svg.fonttype': 'none'}) + self.fig = fig + im = plt.imread(os.path.dirname(__file__) + '/../graphics/LK_logo2.png') + newax = fig.add_axes([0.04, 0.02, 0.10, 0.08]) + newax.imshow(im, interpolation='hanning') + newax.axis('off') + + self.responses = None + + def plot_nothing(self): + self.fig.clf() + + def add_responses(self, responses): + self.responses = responses + + def timehistories(self, subcases, quantities,): + self.fig.clf() + # Create subplots for each state to plot, sharing the x-axis (time). + ax = self.fig.subplots(len(quantities), sharex=True) + # Make sure that ax is always iterable, even if there is only one state to plot. + if len(quantities) == 1: + ax = [ax] + # Plotting the time histories for each state and subcase. + for a, quantity in zip(ax, quantities): + for subcase in subcases: + time = self.responses[subcase]['t'][()] + if quantity in self.states_avail: + # States are stored in 'X' in the same order as in states_avail. + idx = self.states_avail.index(quantity) + data = self.responses[subcase]['X'][:, idx] + elif quantity in self.commands_avail: + # Derive number of mode shapes from modal deformations 'Uf'. + n_modes = self.responses[subcase]['Uf'].shape[1] + # Find commands in state vector/matrix 'X' with the following sequence: + # X = [12 rigid body states, 2*n_modes, 6 commands, many unsteady lag states] + commands = self.responses[subcase]['X'][:, 12 + 2 * n_modes: 12 + 2 * n_modes + 6] + # Commands are stored in the same sequence as in commands_avail + idx = self.commands_avail.index(quantity) + data = commands[:, idx] + elif quantity in self.loadfactors_avail: + # Load factors are stored in 'Nxyz' in the same order as in loadfactors_avail. + idx = self.loadfactors_avail.index(quantity) + data = self.responses[subcase]['Nxyz'][:, idx] + else: + # For the remaining quantities, we need to check if they are available in the response. + # Most quantities have units in their name, so we split by space to obtain the base name for + # lookup in the response. + q = quantity.split()[0] + if quantity in self.other_avail and q in self.responses[subcase]: + data = self.responses[subcase][q][()] + else: + # In case the quantity is not found, create some dummy data. + subcase = 'Not found' + data = np.zeros_like(time) + if '[deg]' in quantity or '[deg/s]' in quantity: + data *= 180.0 / np.pi + # Plot the time history for the current subcase and quantity. + a.plot(time, data, label=subcase) + # Format current axis. + a.ticklabel_format(style='sci', axis='y', scilimits=(-2, 2)) + a.grid(True) + a.set_ylabel(quantity) + # Push left bound to the right to make space for the ylabel. + left, bottom, width, height = a.get_position().bounds + a.set_position([left + 0.05, bottom, width - 0.05, height]) + a.get_yaxis().set_label_coords(x=-0.18, y=0.5) + # Show legend per plot + a.legend(loc='upper right') + ax[-1].set_xlabel('Time [s]') diff --git a/responseviewer/view.py b/responseviewer/view.py new file mode 100644 index 00000000..4a4dc7d9 --- /dev/null +++ b/responseviewer/view.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +import os +import sys + +from PySide6.QtGui import QAction +from PySide6.QtWidgets import ( + QApplication, QWidget, QTabWidget, QSizePolicy, QGridLayout, QMainWindow, QListWidget, + QListWidgetItem, QAbstractItemView, QFileDialog, QMessageBox) +from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg, NavigationToolbar2QT +from matplotlib.figure import Figure + +from responseviewer import plotting +from loadskernel.io_functions import data_handling + + +class ResponseViewer(): + + def __init__(self): + self.responses = None + self.colors = ['cornflowerblue', 'limegreen', 'violet', 'darkviolet', 'turquoise', 'orange', 'tomato', 'darkgrey', + 'black'] + + # define file options + self.file_opt = {} + self.file_opt['filters'] = "HDF5 response files (response*.hdf5);;Pickle response files \ + (response*.pickle);;all files (*.*)" + self.file_opt['initialdir'] = os.getcwd() + self.file_opt['title'] = 'Load Responses' + + # GUI attributes + self.container = None + self.tabs_widget = None + self.canvas = None + self.toolbar = None + self.plotting = None + self.window = None + self.lb_subcase = None + self.lb_states = None + + def run(self): + # Create the app. + app = self.initApp() + # Init the application's menues, tabs, etc. + self.initGUI() + # Start the main event loop. + app.exec() + + def test(self): + """ + This function is intended for CI testing. To test at least some parts of the code, the app is initialized, but never + started. Instead, all windows are closed again. + """ + app = self.initApp() + self.initGUI() + app.closeAllWindows() + + def initApp(self): + # Init the QApplication in a robust way. + # See https://stackoverflow.com/questions/54281439/pyside2-not-closing-correctly-with-basic-example + app = QApplication.instance() + if app is None: + app = QApplication(sys.argv) + return app + + def initGUI(self): + # Use one Widget as a main container. + self.container = QWidget() + # Init all sub-widgets. + self.initMatplotlibFigure() + self.initTabs() + self.initWindow() + # Arrange the layout inside the container. + layout = QGridLayout(self.container) + # Notation: layout.addWidget(widget, row, column, rowSpan, columnSpan) + layout.addWidget(self.tabs_widget, 0, 0, 2, 1) + layout.addWidget(self.canvas, 1, 1) + layout.addWidget(self.toolbar, 0, 1) + + def initTabs(self): + # Configure tabs widget + self.tabs_widget = QTabWidget() + # configure sizing, limit width of tabs widget in favor of plotting area + sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) + self.tabs_widget.setSizePolicy(sizePolicy) + self.tabs_widget.setMinimumWidth(300) + self.tabs_widget.setMaximumWidth(450) + + # Add tabs + self.initStatesTab() + + def initStatesTab(self): + tab_loads = QWidget() + self.tabs_widget.addTab(tab_loads, 'Time Histories') + # Elements of loads tab + self.lb_subcase = QListWidget() + self.lb_subcase.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.lb_subcase.itemSelectionChanged.connect(self.show_choice) + + self.lb_states = QListWidget() + self.lb_states.setSelectionMode(QAbstractItemView.ExtendedSelection) + for item in self.plotting.all_quantities: + self.lb_states.addItem(QListWidgetItem(item)) + self.lb_states.setCurrentRow(4) + self.lb_states.itemSelectionChanged.connect(self.show_choice) + + layout = QGridLayout(tab_loads) + # Notation: layout.addWidget(widget, row, column, rowSpan, columnSpan) + layout.addWidget(self.lb_subcase, 0, 0, 1, 2) + layout.addWidget(self.lb_states, 1, 0, 1, 2) + + def initMatplotlibFigure(self): + # init Matplotlib Plot + fig1 = Figure() + # hand over subplot to plotting class + self.plotting = plotting.Plotting(fig1) + # embed figure + self.canvas = FigureCanvasQTAgg(fig1) + self.canvas.draw() + # configure sizing, set minimum size + sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) + self.canvas.setSizePolicy(sizePolicy) + self.canvas.setMinimumWidth(800) + self.canvas.setMinimumHeight(600) + + self.toolbar = NavigationToolbar2QT(self.canvas, self.container) + self.toolbar.update() + + def initWindow(self): + # Set up window and menu + self.window = QMainWindow() + mainMenu = self.window.menuBar() + # Add Menu to window + fileMenu = mainMenu.addMenu('File') + # Add load button + action = QAction('Load Responses', self.window) + action.setShortcut('Ctrl+L') + action.triggered.connect(self.load_response) + fileMenu.addAction(action) + + # Add exit button + action = QAction('Exit', self.window) + action.setShortcut('Ctrl+Q') + action.triggered.connect(self.window.close) + fileMenu.addAction(action) + + self.window.setCentralWidget(self.container) + self.window.setWindowTitle("Response Viewer") + self.window.show() + + def show_choice(self): + # called on change in listbox, combobox, etc + self.update_plot() + + def update_plot(self): + if self.lb_subcase.currentItem() is not None and self.lb_states.currentItem() is not None: + # Get the items selected by the user. + subcases_selected = [item.text() for item in self.lb_subcase.selectedItems()] + quantities_selected = [item.text() for item in self.lb_states.selectedItems()] + # Call the plotting function. + self.plotting.timehistories(subcases_selected, quantities_selected) + else: + self.plotting.plot_nothing() + self.canvas.draw() + + def load_response(self): + # open file dialog + filename, _ = QFileDialog.getOpenFileName( + self.window, + self.file_opt['title'], + self.file_opt['initialdir'], + self.file_opt['filters'] + ) + if filename != '': + dataset = None + if '.pickle' in filename: + with open(filename, 'rb') as f: + dataset = data_handling.load_pickle(f) + elif '.hdf5' in filename: + dataset = data_handling.load_hdf5(filename) + else: + QMessageBox.warning(self.window, "Unsupported File", "Please select a .pickle or .hdf5 file.") + + if dataset is not None: + # Store dataset + self.responses = dataset + # Update fields + self.update_fields() + # Update response in plotting class + self.plotting.add_responses(self.responses) + self.file_opt['initialdir'] = os.path.split(filename)[0] + + def update_fields(self): + if self.responses is not None: + self.lb_subcase.clear() + list_of_subcases = list(self.responses) + list_of_subcases.sort(key=int) + self.lb_subcase.addItems(list_of_subcases) + + +def command_line_interface(): + r = ResponseViewer() + r.run() + + +if __name__ == "__main__": + command_line_interface() diff --git a/setup.py b/setup.py index b957c3c4..232f4daf 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ Setup file Install Loads Kernel with core dependencies via: - pip install -e -To use the graphical tools and other features, optional libraries definded as extras are necessary: +To use the graphical tools and other features, optional libraries defined as extras are necessary: - pip install -e [extras] Especially with mpi or the graphical libraries, pip frequently fails. In that case, try to install the packages using a package manager such as conda. @@ -10,56 +10,56 @@ from setuptools import setup, find_packages +with open('README.md', encoding='utf8') as f: + readme = f.read() -def my_setup(): - setup(name='LoadsKernel', - version='2026.01.1', - description="""The Loads Kernel Software allows for the calculation of quasi-steady and dynamic maneuver loads, - unsteady gust loads in the time and frequency domain as well as dynamic landing loads based on a generic landing - gear module.""", - long_description=open('README.md', encoding='utf8').read(), - long_description_content_type='text/markdown', - url='https://github.com/DLR-AE/LoadsKernel', - author='Arne Voß', - author_email='arne.voss@dlr.de', - license='BSD 3-Clause License', - packages=find_packages(), - entry_points={'console_scripts': ['loads-kernel=loadskernel.program_flow:command_line_interface', - 'model-viewer=modelviewer.view:command_line_interface', - 'loads-compare=loadscompare.compare:command_line_interface']}, - include_package_data=True, - package_data={'loadskernel': ['graphics/*.*'], - 'loadscompare': ['graphics/*.*'], }, - # Remember to update the requirements also in the conda feedstock (./recipe/meta.yml) when changing them here! - python_requires='>=3.10', - install_requires=['PanelAero', - 'matplotlib', - 'numpy<2.4.0', # Mayavi / VTK does not support numpy >= 2.4.0, wait for release of VTK 9.6 - 'scipy', - 'h5py', - 'tables', - 'pyyaml', - # Pandas 3.0.0 comes with changes that are not yet supported, - # see https://github.com/DLR-AE/LoadsKernel/issues/86 - 'pandas<3.0.0', - ], - extras_require={'extras': ['mpi4py', - 'mayavi', - 'traits', - 'traitsui', - 'jupyter', - 'pyiges', # only available with pip, not with conda - 'pyfmi', - 'pyside6' - ], - 'test': ['pytest', - 'pytest-cov', - 'jupyter-book', - 'flake8', - 'pylint', - ]}, - ) - - -if __name__ == '__main__': - my_setup() +setup( + name='LoadsKernel', + version='2026.05', + description=("The Loads Kernel Software allows for the calculation of quasi-steady and dynamic maneuver loads, " + "unsteady gust loads in the time and frequency domain as well as dynamic landing loads based on a " + "generic landing gear module."), + long_description=readme, + long_description_content_type='text/markdown', + url='https://github.com/DLR-AE/LoadsKernel', + author='Arne Voß', + author_email='arne.voss@dlr.de', + license='BSD 3-Clause License', + packages=find_packages(), + entry_points={'console_scripts': ['loads-kernel=loadskernel.program_flow:command_line_interface', + 'model-viewer=modelviewer.view:command_line_interface', + 'loads-compare=loadscompare.compare:command_line_interface', + 'response-viewer=responseviewer.view:command_line_interface', + ]}, + include_package_data=True, + package_data={'loadskernel': ['graphics/*.*'], + 'loadscompare': ['graphics/*.*'], }, + # Remember to update the requirements also in the conda feedstock (./recipe/meta.yml) when changing them here! + python_requires='>=3.12', + install_requires=['PanelAero', + 'matplotlib', + 'numpy>2.0,<2.4.0', # Mayavi / VTK does not support numpy >= 2.4.0, wait for release of VTK 9.6 + 'scipy', + 'h5py', + 'tables', + 'pyyaml', + # Pandas 3.0.0 comes with changes that are not yet supported, + # see https://github.com/DLR-AE/LoadsKernel/issues/86 + 'pandas' + ], + extras_require={'extras': ['mpi4py', + 'mayavi', + 'traits', + 'traitsui', + 'pyside6', + 'jupyter', + 'pyfmi', + 'pyiges', # only available with pip, not with conda + ], + 'test': ['pytest', + 'pytest-cov', + 'jupyter-book', + 'flake8', + 'pylint', + ]}, +) diff --git a/tests/list_of_packages.txt b/tests/list_of_packages.txt index 723abaf0..b010ee7a 100644 --- a/tests/list_of_packages.txt +++ b/tests/list_of_packages.txt @@ -3,12 +3,12 @@ # conda install -y -c conda-forge --file ./tests/list_of_packages.txt # Lodas Kernel core dependencies matplotlib -numpy<2.4.0 +numpy>2.0,<2.4.0 scipy h5py pytables pyyaml -pandas<3.0.0 +pandas # Extras, optional mpi4py @@ -23,7 +23,7 @@ pyside6 pytest pytest-cov sqlite -jupyter-book==1.0.4 +jupyter-book # Coding style flake8 diff --git a/tests/test_gui.py b/tests/test_gui.py index 02c51eac..0040a3f8 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -2,7 +2,8 @@ try: from loadscompare import compare - from modelviewer import view + from modelviewer import view as modelviewer + from responseviewer import view as responseview except ImportError: pass @@ -19,5 +20,13 @@ class TestModelViewer(): def test_gui(self): logging.info('Testing Model Viewer') - m = view.Modelviewer() + m = modelviewer.Modelviewer() + m.test() + + +class TestResponseViewer(): + + def test_gui(self): + logging.info('Testing Response Viewer') + m = responseview.ResponseViewer() m.test() diff --git a/tests/test_integration_public.py b/tests/test_integration_public.py index 032cd8a4..4f3c9f15 100644 --- a/tests/test_integration_public.py +++ b/tests/test_integration_public.py @@ -78,3 +78,8 @@ class TestDC3Gust(TestDC3Trim): class TestDC3Flutter(TestDC3Trim): job_name = 'jcl_dc3_flutter' aircraft_name = 'DC3_model' + + +class TestDC3Nastran95(PreMainPostFunctional): + job_name = 'jcl_dc3_Nastran95' + aircraft_name = 'DC3_model' diff --git a/tests/test_unittests.py b/tests/test_unittests.py index 3f9dfa99..24c3d16c 100644 --- a/tests/test_unittests.py +++ b/tests/test_unittests.py @@ -4,6 +4,8 @@ from loadskernel.io_functions import read_bdf from loadskernel.fem_interfaces import fem_helper +from loadskernel import atmosphere +from loadskernel import units @pytest.fixture(name='tmp_output', scope='class') @@ -69,3 +71,16 @@ def test_hyperbolic_distance_metric(): HDM = fem_helper.calc_HDM(lam1, lam2) # Check for numerical similarity with reference values. assert np.allclose(HDM, HDM_ref, rtol=1e-4, atol=1e-4), "Hyperbolic distance metric (HDM) does NOT match reference" + + +def test_reynoldsnumber(): + # Test Reynolds number calculation at sea level. + # This test also covers the dynamic viscosity calculation and parts of the isa atmosphere. + p, rho, T, a = atmosphere.isa(0.0) + v = 70.0 # m/s + l_ref = 1.0 # m + Re = units.reynolds_number(rho, v, l_ref, T) + # Reference value, checked with external tool. + Re_ref = 4792379.49652419 + # Check for numerical similarity with reference values. + assert np.allclose(Re, Re_ref, rtol=1e-4, atol=1e-4), "Reynolds number does NOT match reference"