feat: initial otko import
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled

This commit is contained in:
smillmorel 2026-09-08 02:12:15 -04:00
commit 612936a00b
540 changed files with 174136 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

56
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,56 @@
name: Bug report
description: Report a defect in OTKO
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear and concise description of the bug.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
placeholder: |
1. Open a new project
2. Click 'Draw Frame'
3. ...
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true
- type: input
id: version
attributes:
label: OTKO version
placeholder: 0.0.1
validations:
required: true
- type: input
id: python
attributes:
label: Python version
placeholder: 3.11.7
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating System
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: textarea
id: logs
attributes:
label: Console output / traceback
render: shell

View file

@ -0,0 +1,31 @@
name: Feature request
description: Propose a new feature or improvement
labels: ["enhancement"]
body:
- type: textarea
id: motivation
attributes:
label: Motivation
description: What engineering problem does this solve?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
validations:
required: true
- type: dropdown
id: phase
attributes:
label: Roadmap phase this fits into
options:
- "Phase 1 — Core Model"
- "Phase 2 — OpenSees Service"
- "Phase 3 — 3D Viewport"
- "Phase 4 — Modeling Tools"
- "Phase 5 — Properties"
- "Phase 6 — Analysis Pipeline"
- "Phase 7 — Post-processing"
- "Phase 8 — Earthquake Engineering"
- "Out of scope / new"

26
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,26 @@
## Summary
<!-- One sentence: what does this change? -->
## Linked issue
Closes #
## Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Refactor / cleanup
- [ ] Documentation
- [ ] CI / tooling
## Architectural checklist
- [ ] No `from PySide6` in `core/` or `services/`
- [ ] No `import openseespy` in `core/` or `views/`
- [ ] Public functions have type hints + docstrings
- [ ] Heavy work runs off the GUI thread
## Testing
<!-- How was this verified? Reference tests added or analytical checks. -->

48
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install ruff mypy
- run: ruff check src tests
- run: ruff format --check src tests
test:
needs: lint
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Linux Qt deps
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libxkbcommon-x11-0 libxcb-icccm4 \
libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 \
libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xinerama0 \
libxcb-cursor0 libdbus-1-3 libgl1 libxkbcommon0 xvfb
- run: pip install -e ".[dev]"
- name: Run tests (Linux, headless)
if: runner.os == 'Linux'
run: xvfb-run -a pytest --cov --cov-report=xml -m "not slow"
- name: Run tests (Win/Mac)
if: runner.os != 'Linux'
run: pytest --cov --cov-report=xml -m "not slow"

48
.github/workflows/ci.yml.disabled vendored Normal file
View file

@ -0,0 +1,48 @@
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install ruff mypy
- run: ruff check src tests
- run: ruff format --check src tests
test:
needs: lint
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Linux Qt deps
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libxkbcommon-x11-0 libxcb-icccm4 \
libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 \
libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xinerama0 \
libxcb-cursor0 libdbus-1-3 libgl1 libxkbcommon0 xvfb
- run: pip install -e ".[dev]"
- name: Run tests (Linux, headless)
if: runner.os == 'Linux'
run: xvfb-run -a pytest --cov --cov-report=xml -m "not slow"
- name: Run tests (Win/Mac)
if: runner.os != 'Linux'
run: pytest --cov --cov-report=xml -m "not slow"

63
.gitignore vendored Normal file
View file

@ -0,0 +1,63 @@
# Byte-compiled / optimized
__pycache__/
*.py[cod]
*$py.class
*.so
# Distribution / packaging
.Python
build/
dist/
*.egg-info/
*.egg
pip-wheel-metadata/
# Virtual envs
.venv/
venv/
env/
# Test / coverage
.pytest_cache/
.coverage
.coverage.*
htmlcov/
.cache
.mypy_cache/
.ruff_cache/
# Qt
*.qm
*.pyc
.qt_for_python/
# Compiled .qrc → _rc.py (regenerable)
*_rc.py
# IDEs
.idea/
.vscode/
*.swp
*~
# OS
.DS_Store
Thumbs.db
# Project artifacts
*.log
results/
*.h5
*.hdf5
scratch/
# Local AI agent state (Claude Code, Cursor, etc.)
.claude/
.cursor/
# Local example WIP / verification outputs (not for the public repo)
examples/data/Ex*_tmp/
examples/ozan*.osmodel
examples/ozan_results/
/Ex*.csv
/Ex*.png

25
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,25 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ["--maxkb=500"]
- id: check-merge-conflict
- id: mixed-line-ending
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
hooks:
- id: mypy
additional_dependencies: [pydantic, numpy]
files: ^src/otko/(core|services|viewmodels)/

44
AGENTS.md Normal file
View file

@ -0,0 +1,44 @@
# AGENTS.md — otko
Pre-alpha SAP2000-style desktop GUI for OpenSeesPy. Python 3.10+; **Windows requires 3.12+** (`openseespywin==3.8.0.0` has no 3.11 wheel). Solver pinned: `openseespy==3.8.0.0`.
## Install
```bash
pip install -e ".[gui,dev]" # desktop: Qt + PyVista + dev tools
pip install -e . # headless: core + services only, no Qt (scripts, notebooks, web backends)
python -m otko # launch GUI (src/otko/app.py:run)
```
`scipy`/`pandas` in `[gui]` extras are phantom deps (not imported as of 2026-06) — do not add imports expecting them.
## Architecture (enforced in review — see PR template)
Strict one-way MVVM + services: `views → viewmodels → services → core`.
- `core/` (entities: `project.py`, `geometry/`, `materials/`, `sections/`, `loads/`, `analysis/`, `catalog/`): stdlib + numpy + pydantic only. **No Qt, no openseespy. Period.**
- `services/` (`opensees_runner.py`, `persistence.py`, `results.py`, ...): may use core + h5py + openseespy. **No Qt.**
- `views/`: PySide6/pyvistaqt only. **No direct `import openseespy`** — go through a service.
- `viewmodels/` bridges core↔Qt (signals, `QUndoStack`); `commands/` holds `QUndoCommand` subclasses.
- Rules: public functions need type hints + docstring; new domain entities go through Pydantic validation; ops >50 ms run off the GUI thread (`AnalysisWorker` in QThread, cancel via `isInterruptionRequested()`, results cross threads as lightweight `ResultsHandle` to HDF5).
Runner emits OpenSeesPy commands in fixed order (`docs/architecture.md`): `wipe → model → node → fix → material → section → geomTransf → element → timeSeries → pattern/load → recorder → system/numberer/... → analyze`. Never reorder.
## Verify (in this order)
```bash
ruff check src tests
ruff format src tests # line-length 100, E501 ignored
mypy src/otko/core src/otko/services
pytest -m "not slow" # CI gate: lint → this, on 3.10/3.11/3.12 × ubuntu/windows/macos
```
Focused runs: `pytest tests/unit` (pure logic, ms), `pytest tests/gui -k <name>` (pytest-qt, needs display), `pytest tests/integration -k <name>` (real OpenSeesPy runs). Single test: `pytest tests/unit/test_project.py::test_name -q`. Markers: `gui`, `slow`. Linux GUI tests need `xvfb-run -a pytest ...` plus system Qt libs (see `ci.yml` apt list).
Notes: `tests/conftest.py` auto-`ops.wipe()`s the OpenSees domain between tests (lazy import so core tests stay Qt/OpenSees-free). Coverage omits `views/`. Commit style: Conventional Commits (`feat:`, `fix:`, ...). `pre-commit install` runs ruff + mypy (mypy hook scoped to `core|services|viewmodels`).
## Examples & persistence
- `examples/*.py` are source of truth; `examples/*.osmodel` are generated artifacts (checked in). Never hand-edit `.osmodel` — change the script and regen: `python examples/cantilever.py` (each script saves, reloads, asserts clean round-trip).
- Projects persist as single Pydantic-validated JSON `.osmodel` (diffable); analysis output goes to `*.osresults.h5` (HDF5, one group per case).
- Quick smoke: open `examples/cantilever.osmodel` → run `Tip-Load` → M3 peaks 50 kN·m at fixed end.

37
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,37 @@
# Contributing
Thanks for your interest. This project is in an early phase; the bar for
incoming changes is on architecture cleanliness rather than feature
breadth.
## Dev setup
```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install
```
## Before opening a PR
```bash
ruff check src tests
ruff format src tests
mypy src/otko/core src/otko/services
pytest -m "not slow"
```
## Architectural rules (enforced in review)
1. `core/` may not import Qt or `openseespy`. Period.
2. `services/` may not import Qt.
3. `views/` may not import `openseespy` directly — go through a service.
4. Public functions and methods need type hints and a docstring.
5. New domain entities go through Pydantic validation.
6. Long-running operations (>50 ms) run off the GUI thread.
## Commit style
Conventional Commits — `feat:`, `fix:`, `refactor:`, `docs:`, `test:`,
`chore:`, `ci:`.

661
LICENSE Normal file
View file

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

203
README.md Normal file
View file

@ -0,0 +1,203 @@
<p align="center">
<img src="docs/logo.svg" alt="OTKO" width="640">
</p>
<p align="center">
A modern, SAP2000-style desktop GUI for
<a href="https://openseespydoc.readthedocs.io/">OpenSeesPy</a> —
built for structural and earthquake engineers who want a visual
modeling environment without leaving the OpenSees ecosystem.
</p>
<p align="center">
<em>Status: Pre-alpha. Active development. APIs and file formats will change.</em>
</p>
---
![OTKO main window](docs/screenshots/main_window.png)
## Why
OpenSees is the gold-standard nonlinear FEM solver for earthquake
engineering, but its native interface is Tcl/Python scripts.
OTKO adds a visual front-end so you can:
- Click to draw nodes, frames, supports, and loads on a snapped grid.
- Assign materials, sections, and load patterns through dialogs.
- Run static, modal, pushover, and time-history analyses with progress
and cancel support.
- Inspect results visually — deformed shape, mode shapes, force
diagrams, pushover curves, time-history plots, hysteresis loops.
- Save the model as a single `.osmodel` JSON file that round-trips
cleanly (diff-able in Git, scriptable from Python).
Behind the GUI, the same `core` Pydantic model is fully usable from a
script or Jupyter notebook — the GUI is one frontend, not the only one.
## What works today
- **Modeling** — grids, nodes, frames (elastic + force-based), trusses,
quads, zero-length sections, restraints, equalDOF constraints,
distributed loads, ground motions (`PathTimeSeries` /
`UniformExcitation`).
- **Materials and sections** — `Steel01`, `Steel02`, `Concrete01`,
`Concrete02`, `ElasticPP`, `Hysteretic`, fiber sections (rectangular /
circular patches + rebar layers), `SectionAggregator`,
`BeamWithHinges`.
- **Analyses** — static (load- or displacement-controlled), modal,
displacement-controlled pushover, transient time-history with
mode-1 Rayleigh damping. Chained workflows: gravity preload →
`loadConst -time 0.0` → pushover or transient.
- **Post-processing** — deformed shape (with scale slider), animated
mode shapes, axial / shear / moment diagrams, pushover curves
(in display units), time-history plots, hysteresis loops,
response-spectrum SRSS / CQC, snapshot + video export.
- **Persistence** — projects save as a single JSON `.osmodel` file
(Pydantic-validated, round-trip-clean).
- **Examples** — 20+ verified examples bundled, including the OpenSees
Wiki Examples-1 through Example-4 family and a fiber-section RC frame
pushover. See [`examples/README.md`](examples/README.md).
## Tech stack
| Layer | Library |
| ------------ | ------------------------------------ |
| GUI | PySide6 (Qt 6) |
| 3D viewport | PyVista + pyvistaqt (VTK) |
| 2D plots | pyqtgraph |
| Solver | OpenSeesPy 3.8.0.0 |
| Numerics | NumPy |
| Storage | Pydantic v2 (model), h5py (results) |
| Tests | pytest, pytest-qt |
| Lint / type | ruff, mypy |
## Architecture
Strict MVVM + service layer. The `core` package is pure Python — no Qt,
no OpenSeesPy imports — and is fully unit-testable in isolation.
```
views (Qt) → viewmodels → services (OpenSeesRunner, Persistence) → core (model)
```
See [`docs/architecture.md`](docs/architecture.md) for the long form,
including the canonical OpenSeesPy command sequence the runner emits.
## Install (development)
**Desktop GUI** (includes Qt, PyVista, pyqtgraph, imageio):
```bash
git clone https://github.com/ogunc/otko.git
cd otko
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux / macOS
pip install -e ".[gui,dev]"
```
**Headless / web reuse** (core + services only, no Qt pulled in):
```bash
pip install -e .
```
This installs only the headless base set (pydantic, numpy, h5py, openseespy).
It is the correct install for web backends, scripts, and Jupyter notebooks that
reuse `otko.core` or `otko.services` without the GUI.
Python 3.10+ is required. On Windows use **3.12+** — the `openseespywin==3.8.0.0`
wheel has no 3.11 build (`Requires-Python >=3.12`). Pin both
`openseespy==3.8.0.0` and `openseespywin==3.8.0.0` (already pinned
in `pyproject.toml`).
## Quick start — the 60-second tour
```bash
python -m otko
```
Then:
1. **File → Open** → pick `examples/cantilever.osmodel`.
2. **Analyze → Cases** → run `Tip-Load`.
3. **Display → Show Force Diagram** → component **M3** → linear moment
peaking at 50 kN·m at the fixed end. Component **V2** → constant
-10 kN.
4. **Display → Show Deformed Shape** → the classic cantilever curve.
For a nonlinear walkthrough, open `examples/portal_pushover.osmodel`,
run the `Push-X` case, then **Display → Show Pushover Curve** — you'll
see the elastic ramp followed by a yield plateau as the fiber-section
hinges form at the column bases.
## Run the test suite
```bash
pytest tests/unit # pure-logic tests, milliseconds
pytest tests/gui # Qt event-loop tests (pytest-qt)
pytest tests/integration # real OpenSeesPy runs on bundled examples
```
CI runs lint + the non-`slow` subset on Linux / macOS / Windows
× Python 3.10 / 3.11 / 3.12.
## Roadmap
See [`docs/roadmap.md`](docs/roadmap.md) for the phase-by-phase plan.
Phases 0–7 (modeling, analysis, post-processing) are largely done.
Phase 8 (earthquake-engineering primitives — isolators, ground-motion
library, IDA, fiber-section editor polish) is the active edge.
## We're looking for collaborators
This project is most useful to researchers and engineers who already
work with OpenSees and want a faster path from "idea" to "model" —
**and who would rather build that path together than alone.**
If any of the following sounds like you, please open an issue or
say hi:
- 🌉 **Structural / earthquake engineers** comfortable with OpenSees Tcl
or OpenSeesPy who can spot when a feature is "almost right but not
quite" — that calibration feedback is gold.
- 🧪 **Researchers** running pushover, IDA, or response-spectrum studies
who want to validate the GUI against their hand-built scripts.
- 🐍 **Python / Qt developers** interested in scientific desktop apps,
PyVista / VTK rendering, or Pydantic-driven schema design.
- 📚 **Students** who want to learn structural FEM and modern GUI
architecture at the same time — example walkthroughs and tests are
designed to read as documentation.
- 🎨 **UX / icon designers** willing to help shape the dialog set,
toolbar icons, and overall visual language.
Open issues, bug reports, and reproducible test cases are just as
valuable as code. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the dev
setup and the architectural rules enforced in review.
## License
OTKO is released under the **GNU Affero General Public
License v3.0** ([`LICENSE`](LICENSE)).
Plain-language summary (not legal advice — read the license itself):
- ✅ Use it for **research, education, and personal projects** with no
obligation other than keeping the copyright notice intact.
- ✅ Modify and fork it freely.
- ⚠️ If you **distribute** it, modified or not, you must release your
full source under AGPL-3.0.
- ⚠️ If you **run it as a network service** (e.g. host a modified
version as a SaaS), you must release your modifications under
AGPL-3.0.
In other words: anyone is free to learn from and build on this code,
but commercial forks and proprietary derivatives must contribute their
changes back to the community. If your use case needs a different
arrangement (e.g. a closed-source commercial license), please open an
issue to discuss.
Copyright © 2026 Ozan and contributors.

View file

@ -0,0 +1,331 @@
# ADR-0001 — Import gidopensees Schemas into OTKO
| Field | Value |
|---|---|
| **Status** | Proposed |
| **Date** | 2026-05-22 |
| **Author** | ogunc |
| **Deciders** | Core maintainers |
| **Source project** | [gidopensees](https://github.com/rclab-auth/gidopensees) — AUTh Lab of R/C and Masonry Structures |
---
## 1. Context
### Why gidopensees?
gidopensees is the most complete published schema inventory for the OpenSees
material/element/condition set. It covers 60+ material types, 30+ element
types, and 31 boundary-condition and load types — all expressed as GiD
preprocessor `.mat` / `.cnd` BOOK definitions, with DEPENDENCIES (field
visibility rules), `#UNITS#` annotations, and TKWIDGET hooks for
auto-fill presets and Wiki links.
OTKO currently supports ~34 of these objects (see
`docs/gap-analysis-gidopensees.md`). Importing gidopensees schemas would
close 23 P1 gaps (Phase 8 targets) and 21 P2 gaps without requiring us to
reverse-engineer OpenSeesPy docs for each type.
### What we are NOT doing
This ADR covers **schema definitions only** — Pydantic model fields, field
metadata, and default values. It does not cover:
- The `bas/` Python code-generation templates (separate ADR).
- The `tcl/` TKWIDGET Tcl implementations (separate ADR).
- Any runtime OpenSeesPy command emission — that lives in
`services/opensees_runner.py` and follows naturally once a schema
is accepted.
---
## 2. Decision
### 2.1 New package: `core/catalog/`
A new package `src/otko/core/catalog/` will hold
gidopensees-derived schema definitions alongside curated additions.
```
src/otko/core/catalog/
├── __init__.py # re-exports curated + generated public symbols
├── generated/ # output of the codegen tool — DO NOT edit by hand
│ ├── _header.py # shared attribution header (inserted by codegen)
│ ├── steel.py
│ ├── concrete.py
│ ├── other_uniaxial.py
│ ├── nd_materials.py
│ ├── sections.py
│ └── elements.py
└── curated/ # human-reviewed, hand-edited overrides and additions
├── README.md # explains the curated/ contract
└── …
```
**Existing modules are untouched.** The objects in `core/materials/`,
`core/sections/`, `core/geometry/elements.py` remain authoritative for
every type already supported. Catalog objects enter the UI only after
equivalence is established (see §2.7).
### 2.2 Codegen tool: `tools/gidopensees_import/`
A build-time parser + code-generator lives in
`tools/gidopensees_import/`, outside `src/`:
```
tools/gidopensees_import/
├── README.md
├── parse_mat.py # parses OpenSees.mat into an intermediate IR
├── parse_cnd.py # parses OpenSees.cnd into an intermediate IR
├── codegen.py # renders IR → Pydantic v2 model source files
├── ir.py # intermediate representation dataclasses
└── tests/ # unit tests for the parser and codegen
```
The tool runs once per gidopensees update. Its output (`generated/`) is
committed so CI never requires gidopensees to be present. The tool is
invoked manually by a maintainer:
```bash
python tools/gidopensees_import/codegen.py \
--mat path/to/OpenSees.mat \
--cnd path/to/OpenSees.cnd \
--out src/otko/core/catalog/generated/
```
**Rationale:** keeping the parser outside `src/` prevents it from being
imported at runtime, avoids adding GiD file parsing as a dependency of
the installable package, and makes the "run once, commit output" contract
explicit to contributors.
### 2.3 Attribution
Every file under `core/catalog/generated/` carries this header comment
(inserted by `codegen.py`):
```python
# This file is derived from gidopensees.
# Source: https://github.com/rclab-auth/gidopensees
# Authors: AUTh Lab of R/C and Masonry Structures
# (https://rclab.civil.auth.gr/)
# Modifications: generated by tools/gidopensees_import/codegen.py
```
Files under `curated/` carry a similar header when they derive from
gidopensees content.
### 2.4 `.osmodel` format backward-compatibility
The `.osmodel` JSON format uses Pydantic discriminated unions keyed on a
`"type"` field. New material types from `core/catalog/` appear as new
discriminator values. The union in `core/materials/__init__.py` is
extended only when a catalog type is promoted to stable (see §2.7).
Old project files that do not contain the new discriminator values load
cleanly: Pydantic ignores unknown items in lists when
`model_config = ConfigDict(extra="ignore")`, and the `Project` validator
will log (not raise) on unknown type strings if we add a graceful
fallback.
**Migration path:**
1. Catalog type is added with a temporary discriminator value
(e.g. `"catalog.Concrete04"`).
2. After verification (§2.7) it is promoted to a stable value
(e.g. `"Concrete04"`) and the temporary value is kept as an alias for
one minor version.
3. A `migrate_osmodel.py` script in `tools/` handles the rename if needed.
No existing `.osmodel` file ever breaks on open.
### 2.5 Field metadata for TKWIDGET hooks
gidopensees BOOK definitions contain TKWIDGET directives that fire
auto-fill presets (e.g. `SteelUniaxMaterial::GenerateValues`), Wiki links
(`TK_MaterialWikiInfo`), and the Material Tester dialog
(`TK_MaterialTester`). These are deferred from this ADR.
Each field or model where a TKWIDGET hook is relevant carries a
`json_schema_extra` annotation recording the hook name:
```python
class Concrete04(CatalogEntity):
...
class model_config(ConfigDict):
json_schema_extra = {
"tkwidget_hooks": [
"ConcreteUniaxMaterial::GenerateValues",
"TK_MaterialWikiInfo",
"TK_MaterialTester",
]
}
```
This metadata is visible to future UI layers without coupling `core/` to
Qt. Implementation of the actual preset dialogs and Wiki-link buttons
comes in a later UI phase.
### 2.6 DEPENDENCIES → `dependent_schemas` metadata (not validators)
gidopensees BOOK DEPENDENCIES express field-visibility rules:
```
(1, RESTORE, Gap_length, #CURRENT#), (0, HIDE, Gap_length, #CURRENT#)
```
These are **viewmodel / UI concerns**, not data-validity rules, and
therefore must not become Pydantic validators in `core/`.
Each Pydantic model that has visibility dependencies stores them as
`json_schema_extra["dependencies"]` — a list of dicts describing the
trigger field, trigger value, and affected fields. The viewmodel layer
reads these at dialog-construction time to wire up the show/hide logic.
**Example:**
```python
class ViscousDamper(CatalogEntity):
activate_gap: Literal[0, 1] = 0
gap_length: float | None = None
model_config = ConfigDict(
json_schema_extra={
"dependencies": [
{"trigger": "activate_gap", "value": 1, "restore": ["gap_length"]},
{"trigger": "activate_gap", "value": 0, "hide": ["gap_length"]},
]
}
)
```
### 2.7 Unit-annotated fields (`#UNITS#`)
gidopensees marks numeric fields with `#UNITS#` where the value's
interpretation is unit-system–dependent (forces in kN, lengths in m, etc.).
OTKO already has `core/units.py` with `UnitSystem` and
`UnitLabels`. It does **not** currently attach unit metadata to individual
model fields — the unit system is a project-level property and all numeric
values are stored in the project's native unit system, with `UnitLabels`
used only for display.
**Intended design:** Generated catalog models will mark unit-annotated fields
using a `Field` `metadata` entry (Pydantic v2 `Annotated` style):
```python
from typing import Annotated
from pydantic import Field
class UnitTag:
"""Marker for fields whose display label depends on UnitSystem."""
def __init__(self, quantity: str):
self.quantity = quantity # e.g. "force", "length", "stress"
ForceMagnitude = Annotated[float, UnitTag("force")]
LengthValue = Annotated[float, UnitTag("length")]
StressValue = Annotated[float, UnitTag("stress")]
```
These type aliases would live in `core/catalog/_units.py`. No conversion logic
is added to `core/`; the viewmodel layer reads `UnitTag.quantity` to
select the right `UnitLabels` field for axis labels and input hints.
**Implementation status (2026-05-22) — DEFERRED:** The codegen tool does not
yet emit `UnitTag` annotations. Fields corresponding to `#UNITS#` entries in
the gidopensees source are currently emitted as `str` with a
`# TODO: unit-aware type` comment preserving the gidopensees default string
(e.g. `yield_stress_fy: str = '500 MPa' # TODO: unit-aware type`). This is
a conscious deferral: the `str` placeholder keeps the field present and
round-trippable without binding the codebase to a unit-system convention that
is not yet finalised. A dedicated unit-system layer — covering `UnitTag`,
`_units.py`, and viewmodel wiring — is tracked as future work and will be
addressed in a follow-up ADR before any `#UNITS#` field is promoted to stable.
**If this convention is inadequate** (e.g. if we need per-field unit
conversion in the future), a follow-up ADR should address it before
the convention is applied beyond `catalog/`.
### 2.8 Verification plan
A catalog type is promoted from generated → stable only when **all three**
of the following are satisfied:
1. **Schema equivalence test** (`tests/unit/catalog/test_<name>_schema.py`):
Constructs a model instance with the same arguments as the
corresponding Tcl example from the OpenSees Wiki and asserts that
`model.model_dump()` produces the expected dict. No OpenSeesPy import.
2. **Round-trip test** (`tests/unit/catalog/test_<name>_roundtrip.py`):
Serialises the model to JSON (`.osmodel` fragment), deserialises it
back, and asserts equality. Confirms the discriminator and all field
aliases survive the round-trip.
3. **Integration smoke test** (`tests/integration/catalog/test_<name>.py`):
Builds a minimal project using the new type, runs it through
`OpenSeesRunner`, and checks that the runner does not raise and that at
least one result quantity (reaction, displacement, or force) is finite.
Tagged `@pytest.mark.slow` and skipped if `openseespy` is not installed.
Manual review checklist (for the PR that promotes a type):
- [ ] Attribution header present in the generated file.
- [ ] `json_schema_extra["tkwidget_hooks"]` populated where applicable.
- [ ] `json_schema_extra["dependencies"]` populated for every DEPENDENCY
in the source BOOK.
- [ ] `#UNITS#` fields use the correct `UnitTag` quantity string.
- [ ] The type discriminator value does not collide with any existing type
in `core/materials/__init__.py`, `core/sections/__init__.py`, or
`core/geometry/elements.py`.
- [ ] The integration smoke test result has been spot-checked against the
gidopensees wiki reference or an independent OpenSees Tcl run.
---
## 3. Alternatives considered
### 3A: Extend existing `core/materials/__init__.py` directly
Rejected. The existing module is small and well-tested; adding 60+
unverified types creates noise and makes equivalence tracking harder.
A separate `catalog/` namespace keeps the boundary clear.
### 3B: Use gidopensees at runtime (import `.mat` on startup)
Rejected. The GiD BOOK format is proprietary and requires the GiD parser.
Depending on gidopensees at runtime adds a third-party dependency to the
installed package and makes offline / air-gapped installs harder. The
codegen + committed-output approach keeps the package dependency-clean.
### 3C: Hand-write every new type without the codegen tool
Would work but loses the systematic relationship between gidopensees
DEPENDENCIES / TKWIDGET metadata and the Pydantic model. The codegen
pipeline preserves that metadata structurally so UI implementors can
reference it rather than re-reading `.mat` files.
---
## 4. Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Namespace collision between `catalog/` and existing `core/` types | Medium | Medium | Verify discriminator values before promotion; CI check added to the merge checklist |
| gidopensees schema drift (upstream changes `.mat`) | Low | Medium | `generated/` is committed; only re-run codegen intentionally; diff the output and add a CHANGELOG entry |
| Testing surface explosion (60+ new types × 3 test tiers) | High | Low | Only promoted types get full test coverage; generated-but-not-yet-promoted types have schema + round-trip tests only |
| Attribution omission | Low | High | Codegen always inserts the header; a pre-commit hook (`grep -r "rclab-auth/gidopensees" core/catalog/generated/` must pass) enforces it |
| `#UNITS#` convention inadequacy | Medium | Medium | Convention is isolated to `core/catalog/_units.py`; a follow-up ADR can replace it without touching existing `core/` code |
---
## 5. Out of scope for this ADR
- `bas/` Python template parsing and code generation (separate ADR).
- `tcl/` TKWIDGET Tcl implementations (separate ADR).
- Seismic isolator element schemas (`elastomericBearing*`,
`frictionPendulumBearing`, etc.) — those are new elements, not directly
in the gidopensees BOOK format; they get their own ADR.
- IDA batch runner (analysis feature, not schema).
- Fiber-section visual editor UI polish (UI feature, not schema).
- The OpenSeesPy command emission side — `services/opensees_runner.py`
will need updates for each promoted type, but those changes follow
naturally from the schema and are reviewed in the same PR as the
integration smoke test.

View file

@ -0,0 +1,91 @@
# ADR-0002 — Split pyproject dependencies into headless base and `gui` optional extra
| Field | Value |
|---|---|
| **Status** | Accepted |
| **Date** | 2026-06-15 |
| **Author** | ogunc |
---
## 1. Context
`otko.core` is pure Pydantic v2 (no Qt, no OpenSeesPy imports — stated
explicitly in `core/__init__.py`). `otko.services` adds NumPy, h5py, and
OpenSeesPy for headless computation. Together, these two packages can be used from
scripts, Jupyter notebooks, and web backends **without any Qt or 3D-rendering stack**.
Before this ADR, every `pip install otko` pulled in PySide6, pyvista,
pyvistaqt, vtk, pyqtgraph, and imageio — roughly 800 MB of GUI/visualization
packages — even when only the headless computation layer was needed. Web backends and
CI machines without a display had to work around this with `--no-deps`, which is
fragile and skips genuine compute-layer deps (numpy, h5py) too.
## 2. Decision
Split `[project.dependencies]` into two tiers in `pyproject.toml`:
### Headless base (`pip install -e .`)
Packages imported by `core/` and the non-Qt parts of `services/`:
| Package | Where used |
|---------|-----------|
| `pydantic>=2.5` | All `core/` modules, `material_tester.py` |
| `numpy>=1.26` | `services/` computation modules (7 files) |
| `h5py>=3.10` | `opensees_runner._run_transient()`, `TransientResults` accessors |
| `openseespy==3.8.0.0` | Lazy import in `OpenSeesRunner.__init__` |
| `openseespywin==3.8.0.0 ; sys_platform=='win32'` | Windows DLL companion |
### GUI extra (`pip install -e ".[gui]"`)
Packages only needed by `views/`, `viewmodels/`, `commands/`, `qt_workers.py`,
and `animation_export.py` (which drives a live PyVista plotter):
`PySide6`, `pyvista`, `pyvistaqt`, `vtk`, `pyqtgraph`, `imageio[ffmpeg]`,
`scipy` (forward-compat, currently a phantom dep), `pandas` (same).
## 3. Consequences
- **Desktop developers** install with `pip install -e ".[gui,dev]"`. No change to
what gets installed; only the install command changes from `.[dev]` → `.[gui,dev]`.
- **Web backends / scripts / notebooks** install with `pip install -e .` (or
`pip install otko`) and get a lean ~50 MB environment.
- **otko-web** can drop the `--no-deps` workaround and install the
package normally. The web backend's `requirements.txt` no longer needs to list
pydantic/numpy/h5py separately — they come from the base install.
- **scipy and pandas** are listed under `[gui]` as phantom deps (currently never
imported anywhere in the codebase). They are kept to avoid surprise breakage if a
future feature adds them; audited and flagged on 2026-06-15.
## 4. Verification
After installing only the base set:
```python
import sys
from otko.core import Project
from otko.services.opensees_runner import OpenSeesRunner
from otko.services.material_tester import test_uniaxial_material
# Run a modal analysis on the bundled two-storey shear frame example
import json
from pathlib import Path
from otko.core import ModalCase
data = json.loads(Path("examples/eigen_two_storey_shear_frame.osmodel").read_text())
project = Project.model_validate(data)
modal_case = next(c for c in project.analyses if isinstance(c, ModalCase))
runner = OpenSeesRunner(project)
runner.build()
results = runner._run_modal(modal_case)
assert len(results.eigenvalues) == 2
assert all(ev > 0 for ev in results.eigenvalues)
gui_packages = {"PySide6", "pyvista", "pyvistaqt", "vtk", "pyqtgraph"}
assert not gui_packages.intersection(sys.modules), \
f"GUI package imported: {gui_packages & sys.modules.keys()}"
print("PASS — headless modal analysis complete, no GUI packages imported")
```

80
docs/architecture.md Normal file
View file

@ -0,0 +1,80 @@
# Architecture
## Layering
OTKO uses a strict **MVVM + service layer** architecture. Dependencies
flow in **one direction only**: outer layers may depend on inner layers, never
the reverse.
```
┌─────────────────────────────────────────────────────────────────┐
│ views/ Qt widgets, dialogs, 3D canvas — PySide6 only │
│ ▲ │
│ │ signals/slots, viewmodel binding │
│ viewmodels/ Qt-aware adapters, QUndoStack, selection state │
│ ▲ │
│ │ pure Python calls │
│ services/ OpenSeesRunner, PersistenceService, Results │
│ ▲ │
│ │ │
│ core/ Project, Node, Element, Material — pure Python │
│ NO Qt imports. NO openseespy imports. │
└─────────────────────────────────────────────────────────────────┘
```
### Why this matters
- The `core` package is testable without a display server, without OpenSees,
and without Qt. CI runs `pytest tests/unit/` in milliseconds.
- Replacing OpenSeesPy with another solver (e.g. `xara`, a future fork) only
touches `services/opensees_runner.py`.
- A future CLI or Jupyter frontend reuses `core` and `services` unchanged.
## Package map
| Package | Responsibility | Allowed imports |
|---|---|---|
| `core` | Domain entities and invariants | stdlib, numpy, pydantic |
| `services` | I/O, solver invocation, persistence | core + stdlib + h5py + openseespy |
| `viewmodels` | Bridge core ↔ Qt; expose Qt signals; manage undo/redo | core, services, PySide6 |
| `views` | Pure UI; no business logic | PySide6, pyvistaqt, viewmodels |
| `commands` | `QUndoCommand` subclasses; mutate model via services | services, viewmodels |
## Threading
The Qt main thread owns all widgets. Heavy computation happens elsewhere:
- **OpenSees analysis** runs in a `QThread` worker (`services.opensees_runner.AnalysisWorker`).
- The worker emits `progress(int)`, `log(str)`, `finished(ResultsHandle)` signals.
- The worker checks `QThread.currentThread().isInterruptionRequested()` between
analysis steps so the user can cancel.
- Results are written to HDF5; only a lightweight `ResultsHandle` (file path +
metadata) crosses the thread boundary.
## Persistence
- Project files: `*.osmodel` — a JSON document validated by Pydantic models.
Human-readable, diff-able, version-controllable.
- Results files: `*.osresults.h5` — HDF5; one group per analysis case; datasets
for displacements, reactions, element forces, stresses.
## OpenSeesPy command sequencing
`OpenSeesRunner` always emits commands in this order; the model layer enforces
that all required pieces exist before a run can be requested:
1. `wipe()` and `model('basic', '-ndm', ndm, '-ndf', ndf)`
2. `node(...)` for every node
3. `fix(...)` for every restrained DOF
4. `uniaxialMaterial(...)` / `nDMaterial(...)`
5. `section(...)` (if used)
6. `geomTransf(...)` for frame elements
7. `element(...)` for every element
8. `timeSeries(...)`
9. `pattern(...)` with nested `load(...)`
10. `recorder(...)`
11. `system / numberer / constraints / integrator / algorithm / analysis`
12. `analyze(...)`
Any deviation from this order is a runtime error in OpenSees. The runner
asserts the order at the service boundary; the UI never has to think about it.

View file

@ -0,0 +1,221 @@
# Gap Analysis — OTKO vs gidopensees
**Source:** `D:\GitHub\gidopensees` (AUTh Lab of R/C and Masonry Structures)
**Scope:** Material / section / element / constraint / load / damping schema coverage.
**Date:** 2026-05-22
## How to read this table
| Column | Meaning |
|---|---|
| **Category** | Schema group (material family, element type, etc.) |
| **Object** | Name as it appears in gidopensees BOOK/CONDITION |
| **OTKO name** | Corresponding class in `core/` (if any) |
| **In Studio?** | ✅ fully supported · 🟡 partial · ❌ missing |
| **In gidopensees?** | ✅ · ❌ |
| **Priority** | P0 = already done · P1 = Phase 8 target · P2 = later |
Priority rationale:
- **P0** — already shipped; included for completeness.
- **P1** — high-value for earthquake-engineering practice; aligns with Phase 8
roadmap items (isolators, Rayleigh per-region, confined concrete models,
shell elements, floor diaphragm constraints).
- **P2** — valid but lower-frequency in typical EQ-engineering workflows
(soil p-y/t-z/q-z springs, 3-D solid elements, multi-yield plasticity,
contact elements).
---
## 1. Uniaxial Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Uniaxial / linear | Elastic | `ElasticUniaxial` | ✅ | ✅ | P0 |
| Uniaxial / elastic-plastic | Elastic_Perfectly_Plastic | `ElasticPP` | ✅ | ✅ | P0 |
| Uniaxial / elastic-plastic | Elastic_Perfectly_Plastic_with_Gap | — | ❌ | ✅ | P1 |
| Uniaxial / damper | Viscous | — | ❌ | ✅ | P1 |
| Uniaxial / damper | Viscous_Damper (Maxwell) | — | ❌ | ✅ | P1 |
| Uniaxial / gap | Hyperbolic_Gap | — | ❌ | ✅ | P2 |
| Uniaxial / soil | PySimple1 | — | ❌ | ✅ | P2 |
| Uniaxial / soil | TzSimple1 | — | ❌ | ✅ | P2 |
| Uniaxial / soil | QzSimple1 | — | ❌ | ✅ | P2 |
| Uniaxial / bond-slip | BondSP01 | — | ❌ | ✅ | P2 |
## 2. Steel Uniaxial Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Steel | Steel01 | `Steel01` | ✅ | ✅ | P0 |
| Steel | Steel02 | `Steel02` | ✅ | ✅ | P0 |
| Steel | Hysteretic | `HystereticMaterial` | ✅ | ✅ | P0 |
| Steel | Reinforcing_steel (DoDD-Restrepo) | — | ❌ | ✅ | P1 |
| Steel | Ramberg-Osgood_steel | — | ❌ | ✅ | P2 |
## 3. Concrete Uniaxial Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Concrete | Concrete01_(Zero_tensile_strength) | `Concrete01` | ✅ | ✅ | P0 |
| Concrete | Concrete02_(Linear_tension_softening) | `Concrete02` | ✅ | ✅ | P0 |
| Concrete | Concrete04_(Popovics) | — | ❌ | ✅ | P1 |
| Concrete | Concrete06 | — | ❌ | ✅ | P2 |
| Concrete | ConcreteCM (Chang-Mander) | — | ❌ | ✅ | P1 |
## 4. Combined Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Combination | Series | — | ❌ | ✅ | P1 |
| Combination | Parallel | — | ❌ | ✅ | P1 |
| Combination | Section_Aggregator (in .mat) | `SectionAggregator` | ✅ | ✅ | P0 |
## 5. nD (Multi-dimensional) Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| nD | Elastic_Isotropic | `ElasticIsotropic` | ✅ | ✅ | P0 |
| nD | Elastic_Orthotropic | — | ❌ | ✅ | P2 |
| nD | J2Plasticity | — | ❌ | ✅ | P2 |
| nD | Damage2p | — | ❌ | ✅ | P2 |
| nD | PressureIndependMultiYield | — | ❌ | ✅ | P2 |
| nD | PressureDependMultiYield | — | ❌ | ✅ | P2 |
| nD | PressureDependMultiYield02 | — | ❌ | ✅ | P2 |
| nD | Contact | — | ❌ | ✅ | P2 |
## 6. Sections
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Section | Elastic_Section | `ElasticSection` | ✅ | ✅ | P0 |
| Section | Fiber | `FiberSection` | ✅ | ✅ | P0 |
| Section | Fiber_Custom | `FiberSection` (manual fibres) | 🟡 | ✅ | P0 |
| Section | FiberInt (interaction P-M) | — | ❌ | ✅ | P1 |
| Section | Plate_Fiber | — | ❌ | ✅ | P2 |
| Section | Elastic_Membrane_Plate | — | ❌ | ✅ | P2 |
| Section | LayeredShell | — | ❌ | ✅ | P2 |
| Section | Section_Aggregator | `SectionAggregator` | ✅ | ✅ | P0 |
## 7. Beam-Column Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Frame | Elastic_Beam-Column | `ElasticBeamColumn` | ✅ | ✅ | P0 |
| Frame | Elastic_Timoshenko_Beam-Column | — | ❌ | ✅ | P1 |
| Frame | Force-Based_Beam-Column | `ForceBeamColumn` | ✅ | ✅ | P0 |
| Frame | Displacement-Based_Beam-Column | `DispBeamColumn` | ✅ | ✅ | P0 |
| Frame | Flexure-Shear_Interaction_DispBeamColumn | — | ❌ | ✅ | P2 |
| Frame | BeamWithHinges | `BeamWithHingesElement` | ✅ | ❌ | P0 |
## 8. Truss Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Truss | Truss | `TrussElement` | ✅ | ✅ | P0 |
| Truss | Corotational_Truss | `CorotTrussElement` | ✅ | ✅ | P0 |
## 9. Surface / Plate Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Surface | Quad | `QuadElement` | ✅ | ✅ | P0 |
| Surface | Shell (ShellMITC4 / MITC4) | — | ❌ | ✅ | P1 |
| Surface | ShellDKGQ | — | ❌ | ✅ | P1 |
| Surface | Tri31 | — | ❌ | ✅ | P2 |
| Surface | QuadUP (u-p pore pressure) | — | ❌ | ✅ | P2 |
## 10. Solid Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Solid | Standard_Brick_Element | — | ❌ | ✅ | P2 |
## 11. Zero-Length / Special Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Special | Auto_Zero_Length (per-DOF uniaxial) | `ZeroLengthElement` | ✅ | ✅ | P0 |
| Special | Auto_equal_constraint (auto equalDOF) | `EqualDOFConstraint` | ✅ | ✅ | P0 |
| Special | ZeroLengthSection | `ZeroLengthSectionElement` | ✅ | ❌ | P0 |
| Special | BeamContact (master/slave) | — | ❌ | ✅ | P2 |
## 12. Restraints (Boundary Conditions)
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Restraint | Point_Restraints | Node.restraint (6-tuple) | ✅ | ✅ | P0 |
| Restraint | Line_Restraints (auto-apply to nodes on line) | — | ❌ | ✅ | P2 |
| Restraint | Surface_Restraints (auto-apply to nodes on surface) | — | ❌ | ✅ | P2 |
## 13. Nodal Loads & Displacements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Load | Point_Forces | `NodalLoad` | ✅ | ✅ | P0 |
| Load | Line_Forces (nodal, along a line) | — | ❌ | ✅ | P2 |
| Load | Surface_Forces (nodal, on a surface) | — | ❌ | ✅ | P2 |
| Load | Line_Uniform_Forces | `UniformElementLoad` | ✅ | ✅ | P0 |
| Load | Point_Displacements (imposed) | — | ❌ | ✅ | P1 |
| Load | Line_Displacements (imposed, on nodes along line) | — | ❌ | ✅ | P2 |
| Load | Surface_Displacements (imposed, on nodes on surface) | — | ❌ | ✅ | P2 |
## 14. Ground Motions
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Ground motion | Point_Ground_Motion_from_Record | `PathTimeSeries` + `UniformExcitationPattern` | ✅ | ✅ | P0 |
| Ground motion | Point_Sine_Ground_Motion | — (no `TrigTimeSeries`) | ❌ | ✅ | P1 |
| Ground motion | Records (BOOK 8 — ground motion file library) | `PathTimeSeries.file_path` (single file, no library) | 🟡 | ✅ | P1 |
## 15. Constraints
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Constraint | Point_Equal_constraint (master + slave) | `EqualDOFConstraint` | ✅ | ✅ | P0 |
| Constraint | Line_Equal_constraint (slave nodes on line) | — | ❌ | ✅ | P1 |
| Constraint | Point_Rigid_link (Bar / Beam) | — | ❌ | ✅ | P1 |
| Constraint | Line_Rigid_link (slave nodes on line) | — | ❌ | ✅ | P1 |
| Constraint | Point_Rigid_diaphragm (master + slave, XY/YZ/ZX plane) | — | ❌ | ✅ | P1 |
| Constraint | Line_Rigid_diaphragm (slave nodes on line) | — | ❌ | ✅ | P1 |
## 16. Mass
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Mass | Point_Mass | node mass (Properties dock + SetMassCommand) | ✅ | ✅ | P0 |
| Mass | Line_Mass (auto-lump to nodes) | — | ❌ | ✅ | P1 |
| Mass | Surface_Mass | — | ❌ | ✅ | P2 |
| Mass | Volume_Mass | — | ❌ | ✅ | P2 |
## 17. Rayleigh Damping
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Damping | Global αM + βK (TransientCase fields) | `TransientCase.rayleigh_alpha_m/beta_k` | ✅ | 🟡 | P0 |
| Damping | Mode-1 stiffness-proportional βK auto-compute | `TransientCase.rayleigh_mode1_damping` | ✅ | ❌ | P0 |
| Damping | Per-region Rayleigh (Line/Surface/Volume/Point) | — | ❌ | ✅ | P1 |
---
## Summary
| Status | Count |
|---|---|
| ✅ Fully in OTKO | 34 |
| 🟡 Partial | 3 |
| ❌ P1 targets (Phase 8 additions) | 23 |
| ❌ P2 deferred | 21 |
**Top P1 targets** (highest EQ-engineering impact, not in Studio yet):
1. `ElasticPP_with_Gap` — bearing pad / isolation gap nonlinearity
2. `Viscous` / `Viscous_Damper` — supplemental damping devices
3. `ReinforcingSteel` — DoDD-Restrepo model for well-detailed rebar
4. `Concrete04` (Popovics) / `ConcreteCM` (Chang-Mander) — better confined concrete
5. `Series` / `Parallel` — material combination building blocks for isolation systems
6. `RigidDiaphragm` — floor slab constraint, essential for 3D building models
7. `RigidLink` — column/beam offset rigid connections
8. `Shell` (MITC4 / ShellDKGQ) — wall / slab elements
9. `FiberInt` — P-M interaction section for axial-flexure coupling
10. `PointDisplacement` imposed load — displacement-based loading at nodes
11. Per-region Rayleigh damping — finer damping control for mixed models
12. Sine ground motion / ground motion record library — GM workflow completion

46
docs/logo.svg Normal file
View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 180" width="720" height="180" role="img" aria-label="OTKO">
<title>OTKO</title>
<desc>Wordmark logo for OTKO — a SAP2000-style desktop GUI for OpenSeesPy.</desc>
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#1F2A3D"/>
<stop offset="100%" stop-color="#0F1620"/>
</linearGradient>
<linearGradient id="frameStroke" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#9DC8FF"/>
<stop offset="100%" stop-color="#4F92E8"/>
</linearGradient>
<linearGradient id="deformed" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#4F92E8" stop-opacity="0.0"/>
<stop offset="50%" stop-color="#4F92E8" stop-opacity="0.6"/>
<stop offset="100%" stop-color="#4F92E8" stop-opacity="0.0"/>
</linearGradient>
</defs>
<!-- Background panel — keeps contrast consistent on light and dark GitHub themes -->
<rect x="0" y="0" width="720" height="180" rx="14" fill="url(#bg)"/>
<!-- Mark: portal frame with a deformed-shape ghost -->
<g transform="translate(40, 36)" stroke-linecap="round" stroke-linejoin="round" fill="none">
<path d="M 6 100 C 22 100, 22 12, 60 12 L 60 12 C 98 12, 98 100, 114 100"
stroke="url(#deformed)" stroke-width="5" stroke-dasharray="3 5"/>
<path d="M 6 100 L 6 8 L 114 8 L 114 100"
stroke="url(#frameStroke)" stroke-width="7"/>
<rect x="-3" y="100" width="18" height="10" fill="#F2A93B" rx="1"/>
<rect x="105" y="100" width="18" height="10" fill="#F2A93B" rx="1"/>
<circle cx="6" cy="8" r="5" fill="#E8EDF5"/>
<circle cx="114" cy="8" r="5" fill="#E8EDF5"/>
</g>
<!-- Wordmark -->
<g font-family="Segoe UI, Inter, Helvetica, Arial, sans-serif">
<text x="200" y="92" font-size="56" font-weight="700" letter-spacing="-1">
<tspan fill="#E8EDF5">Open</tspan><tspan fill="#7BB1F0">Sees</tspan><tspan fill="#E8EDF5"> Studio</tspan>
</text>
<text x="202" y="124" font-size="16" font-weight="500" letter-spacing="3" fill="#8FA2BF">
A SAP2000-STYLE GUI FOR OPENSEESPY
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

198
docs/roadmap.md Normal file
View file

@ -0,0 +1,198 @@
# Roadmap
OTKO is built in eight phases. Phases 0–7 ship the core GUI
plus all the post-processing tooling we need for verification work.
Phase 8 layers in the earthquake-engineering primitives that turn the
GUI from "OpenSees frontend" into a usable research tool.
Status legend: ✅ done · 🟡 partial · ⬜ planned · ✂️ deferred / out-of-scope.
## Phase 0 — Scaffolding ✅
- ✅ Repo, `.gitignore`, `pyproject.toml`
- ✅ Pre-commit + ruff + mypy
- ✅ GitHub Actions CI (Linux/Mac/Win × Py 3.10–3.12)
- ✅ `python -m otko` opens a `MainWindow` with PyVista 3D
viewport, model-tree dock, property dock, console dock, working-plane
toolbar, and a full menu bar (File / Edit / Define / Assign /
Analyze / Display / View / Options / Help)
- 🟡 Application icon and About dialog — wordmark logo done; native
OS icon (`.ico` / `.icns`) still pending
## Phase 1 — Core Data Model ✅
- ✅ `core.geometry`: `Node`, `Element`, `TrussElement`, `CorotTrussElement`,
`ElasticBeamColumn`, `ForceBeamColumn`, `DispBeamColumn`,
`ZeroLengthElement`, `ZeroLengthSectionElement`, `BeamWithHingesElement`,
`QuadElement`, plus `GridSystem` / `CoordinateSystem`
- ✅ `core.materials`: `ElasticIsotropic`, `ElasticUniaxial`, `ElasticPP`,
`Steel01`, `Steel02`, `Concrete01`, `Concrete02`, `HystereticMaterial`
- ✅ `core.sections`: `ElasticSection`, `FiberSection` with rectangular
/ circular patches and straight rebar layers, `SectionAggregator`
- ✅ `core.loads`: `NodalLoad`, `UniformElementLoad`, `LinearTimeSeries`,
`ConstantTimeSeries`, `PathTimeSeries`, `PlainLoadPattern`,
`UniformExcitationPattern`, `ResponseSpectrum`
- ✅ `core.analysis`: `StaticCase`, `ModalCase`, `TransientCase`,
`PushoverCase`, `ResponseSpectrumCase` — including chained preload
via `preload_case_ids` and pattern removal for free-vibration runs
- ✅ `core.constraints`: `EqualDOFConstraint` (multi-point constraints)
- ✅ `core.project.Project` aggregator with id allocation, validation,
`validate_references()`
- ✅ `services.persistence`: `.osmodel` (Pydantic JSON) load/save with
round-trip-clean assertion in every example script
## Phase 2 — OpenSees Service ✅
- ✅ `services.opensees_runner.OpenSeesRunner` emits commands in the
canonical order documented in [`architecture.md`](architecture.md)
- ✅ Verified examples (matched analytically or against the OpenSees
Wiki Tcl reference): cantilever (point + UDL), portal frame, basic
truss, SDOF pushover, RC frame gravity / pushover / earthquake,
Examples 1–4 family, two-storey shear / one-bay frames, simply
supported beam with quad elements
- ✅ `AnalysisWorker(QObject)` runnable inside a `QThread` with
`progress(int)` / `log(str)` / `finished(ResultsHandle)` signals
## Phase 3 — 3D Viewport ✅
- ✅ `views.canvas3d.ModelCanvas` (subclass of `QtInteractor` from pyvistaqt)
- ✅ Grid plane, world axes triad, view-cube-style preset buttons
(Isometric / Top XY / Front XZ / Right YZ), parallel projection toggle
- ✅ Node rendering as glyphs; element rendering as tubes (frames /
trusses) and shells (quads); supports rendered as gizmos
- ✅ Mouse picking → `nodePicked` / `elementPicked` signals; pixel-space
grid snap (rejects clicks more than 15 px from an intersection)
- ✅ Selection highlighting with in-place colour updates
## Phase 4 — Modeling Tools ✅
- ✅ Grid system dialog (X / Y / Z spacing, generates nodes); SAP2000-style
table editor; off-grid clicks rejected
- ✅ Working-plane filter — grid + snap restricted to the active level
- ✅ Draw Node / Draw Frame / Draw Truss tools with hover snap highlight
- ✅ Inline element editing from the Properties dock (truss area,
any scalar field)
- ✅ Assign Support tool (Free / Pin / Roller / Fix + custom 6-DOF dialog)
- ✅ Assign Load: nodal loads, distributed beam loads, ground motions
- ✅ Assign EqualDOF (multi-point constraints) from the UI
- ✅ Show Extruded Sections toolbar shortcut
- ✅ Undo / Redo via `QUndoStack` for every model mutation
- 🟡 Replicate / Mirror / Move / Extrude — basic copy works; story-extrude
and mirror still pending
## Phase 5 — Properties ✅
- ✅ Material library dialog (CRUD)
- ✅ Section library dialog (incl. `FiberSection` rows that previously
crashed are now handled)
- ✅ Property editor dock — context-aware, multi-selection assignment,
inline mass editor
- 🟡 Fiber-section visual editor — exists; some UI polish still needed
## Phase 6 — Analysis Pipeline ✅
- ✅ Analysis case manager dialog with case-type factories
- ✅ Run dialog with progress + log + cancel
- ✅ Per-run Rayleigh damping override (no project mutation)
- ✅ Results stored to `<project>.osresults.h5`
- 🟡 Convergence diagnostics view (residuals per step) — partial info
in run log; dedicated diagnostics dock pending
## Phase 7 — Post-processing ✅
- ✅ Deformed shape with scale-factor slider
- ✅ Mode-shape animator (1-indexed; play / scrub / scale)
- ✅ Element force diagrams (axial, shear, moment) with auto-pick of
the largest-magnitude component on dock open, numerical labels at
global min/max ends
- ✅ Time-history plotter (pyqtgraph) with displacement / velocity /
acceleration switching
- ✅ Hysteresis plotter — node DOF orbits and element local-force loops
- ✅ Pushover curve view in display units
- ✅ Response-spectrum view (Sa-T curve with modal-period markers and a
mass-participation table)
- ✅ Snapshot / video export (mode shapes + time histories) via
`imageio[ffmpeg]`
- ⬜ **Render performance pass** — collapse per-entity actors into glyphed
PolyData (single draw call), in-place colour updates for selection,
AA, lower-tessellation spheres. Target: 10k nodes / 20k frames @ 30 fps
## Phase 8 — Earthquake Engineering 🟡
- ✅ `HystereticMaterial`, `BeamWithHinges`, `FiberSection` → all
flowing into the runner, end-to-end pushover example
- ✅ Response spectrum generator + SRSS / CQC modal combination
- ✅ Ground-motion import via `PathTimeSeries` + `UniformExcitationPattern`,
with an example wired up against the OpenSees A10000 record
- ✅ `ZeroLengthSectionElement` for moment-curvature workflows; closed-form
verification example shipped
- ✅ `Concrete04` (Popovics) end-to-end: model → runner → UI form → tests →
fiber-section cantilever example
- ✅ **Material Tester service** (`services/material_tester.py`) — headless,
Qt-free; runs any uniaxial material through a monotonic or cyclic strain
protocol in an isolated single-element model and returns the full
stress–strain history. Verified: Elastic linearity, ElasticPP plateau,
Steel01 hysteresis energy (EPP formula, <1%), Concrete04 Popovics C1
continuity; state-cleanup and interleave proofs.
- ⬜ **Material Tester dialog** — Qt front-end for the service above; live
stress–strain plot with strain-amplitude and step controls
- ⬜ Seismic isolators: `elastomericBearing*`, `frictionPendulumBearing`,
`singleFPBearing`, `TripleFrictionPendulum`
- ⬜ Ground-motion library (PEER-style record set + scaling tools)
- ⬜ IDA (Incremental Dynamic Analysis) batch runner
- 🟡 Fiber-section editor — exists for rectangular / circular sections;
confined / unconfined visual presets pending
## Out-of-scope (for now)
- ✂️ Code-checking (TBDY-2018, ASCE 41, Eurocode 8)
- ✂️ Soil-structure interaction GUI
- ✂️ Cloud / collaborative editing
- ✂️ Native shell-element rendering / pre-processing (quads exist as a
primitive, but a proper shell workflow is its own phase)
## Next sessions — backlog (Sept 2026 cooldown session)
Session handoff first: ~60 files of uncommitted work in the tree
(Table dock, extrusion shapes, exporter, pattern_factors, all audit
fixes). Commit per Conventional Commits on `develop` before new work
(`feat:`/`fix:` split per lane), then `ruff check src tests &&
ruff format src tests`, `mypy`, `pytest -m "not slow"`.
Requested (user-ordered):
1. ⬜ Toolbar button icons — `resources/icons/` exists; wire `QIcon`s
in `menu_builder.py` toolbar builders (`@designer` lane: layout,
hierarchy, affordances). Include OS icon (`.ico`/`.icns`, Phase 0 🟡).
2. ⬜ Shell objects — analysis first: `QuadElement` today is continuum
(plane stress/strain). Scope OpenSees `ShellMITC4`/`ShellDKGQ` +
shell sections/materials, then core element + `_emit` + renderer
quad→shell + section dialog. Keep solver-source untouched
(emit `-factor`-style floats only). (`@oracle` for the scope call.)
3. ⬜ Input-dialog layout/usability pass — continue the Lane C pattern:
`QFormLayout` consistency, prefill from selection (done for assign
dialogs), inline validation messages instead of silent reverts,
units-aware labels. (`@designer` for layout, orchestrator for copy.)
4. ⬜ Hover tooltips — two halves: (a) canvas entity hover (node/element
id + key values via existing picking signals); (b) widget tooltip
audit (every toolbar button/dialog field documents itself).
5. ⬜ Load visibility filter — show only loads of the selected pattern /
case; hide the rest. Renderer load-overlay filter + selector combo
(builds on the Table Loads tabs' pattern filter). Natural home:
View toolbar next to Show Local Axes.
Identified this session (audit + build leftovers):
6. ⬜ Named load combinations (deferred phase 3) — `pattern_factors`
covers per-case factoring; add a reusable named-combo entity only
if one combo must be shared across many cases.
7. ⬜ Per-row Run + status in Table Analyses tab — run control lives
only in the Run dialog today; add per-case Run button + last-run
status (converged/failed/when). No overlap: the tab lists cases,
this operates them.
8. ⬜ Case-manager edit preservation — `modelMutated` while the manager
is open rebuilds the form and discards in-progress edits (audit C2).
9. ⬜ Table dock phase-4 polish — CSV copy/paste, column visibility
(deferred from the Table plan).
10. ⬜ Display-settings persistence — extruded-sections / local-axes /
parallel-projection toggles reset per project; persist viewport
prefs in `QSettings` (no `DisplaySettings` module exists yet).
11. ⬜ GUI test coverage under xvfb — `views/*` omitted from coverage;
lanes verified via offscreen smoke only. Add pytest-qt tests for:
dock toggles, Level refresh, post-state teardown, Table edits,
factor spins, local-axes overlay.
12. ⬜ Pin ruff version — local ruff (0.15.x) flags pre-existing drift
(UP037/RUF001/RUF003/E702) that repo CI doesn't; pin in
`pyproject.toml` or baseline-allowlist so `ruff check` is green.
13. ⬜ Exported-Tcl round trip — `.py` export is solver-verified;
add an equivalent exec-and-compare test for the `.tcl` renderer.

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

0
examples/.gitkeep Normal file
View file

237
examples/README.md Normal file
View file

@ -0,0 +1,237 @@
# Example models
Pre-built `.osmodel` files plus the Python scripts that produce them.
Each model is set up with whichever case types the post-processing
features need, so you can exercise the full GUI without manually
defining materials, sections, loads, and analysis cases.
## Files
| Model | Nodes | Elements | Cases | Best for demonstrating |
|---|---|---|---|---|
| `cantilever.osmodel` | 6 | 5 | Static × 2, Modal | Point & distributed loads, force diagrams, deformed shape, mode shapes |
| `portal_frame.osmodel` | 4 | 3 | Static, Modal, Transient | All Display features, simplest 3D |
| `space_frame_3d.osmodel` | 12 | 16 | Static, Modal, Transient (5% damping) | Realistic 3D rendering, multiple modes, damped EQ time-history |
| `sdof_pushover.osmodel` | 2 | 1 | Pushover, Modal | Monotonic pushover curve, HystereticMaterial |
| `portal_pushover.osmodel` | 4 | 3 | Pushover, Modal | Fiber sections, BeamWithHinges, nonlinear pushover with yielding |
| `ex1a_canti2d.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Original OpenSees Ex 1a with shared gravity, push, and earthquake cases |
| `ex1b_portal2d.osmodel` | 4 | 3 | Static preload, Pushover, Transient EQ | Original OpenSees Ex 1b elastic portal frame with distributed gravity |
| `ex2a_canti2d_elastic_element.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Variable-driven cantilever example with derived parameters |
| `ex2b_canti2d_inelastic_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | First nonlinear cantilever with aggregated uniaxial section |
| `ex2c_canti2d_inelastic_fiber_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Fiber-section cantilever with coupled axial-flexural nonlinearity |
| `ex3_canti2d_elastic_element.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Example 3 elastic build with unit-scaled parameters |
| `ex3_canti2d_inelastic_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Example 3 aggregated-section nonlinear build |
| `ex3_canti2d_inelastic_fiber_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Example 3 fiber-section nonlinear build |
| `ex4_portal2d_elastic_element.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Example 4 elastic portal frame with separated build/analysis workflow |
| `ex4_portal2d_inelastic_section.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Example 4 aggregated-section portal frame variant |
| `ex4_portal2d_inelastic_fiber_section.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Example 4 fiber-section portal frame variant |
| `ex1a_canti2d_eq.osmodel` | 2 | 1 | Static preload, Transient EQ | OpenSees Ex 1a style gravity + base excitation workflow |
| `eigen_two_storey_shear_frame.osmodel` | 6 | 6 | Modal | equalDOF floor constraints, mode shapes, eigenvalue workflow |
| `eigen_two_storey_one_bay_frame.osmodel` | 6 | 6 | Modal | classic elastic frame modal example, sway mode shapes |
| `concrete04_cantilever.osmodel` | 2 | 1 | Static (gravity), Pushover | Popovics Concrete04 fiber section; proof-of-concept for the Concrete04 end-to-end stack |
## Quick tour
### 1. Force diagrams — `cantilever.osmodel`
```
File → Open → cantilever.osmodel
Analyze → Cases → run "Tip-Load"
Display → Show Force Diagram → component "M3" → linear moment, max at fixed end (50 kN·m)
→ component "V2" → constant -10 kN along the whole span
→ component "N" → ~zero (no axial load applied)
→ component "T" → ~zero (no torsion → console hint, no diagram)
Display → Show Deformed Shape → classic cantilever curve
```
The load is applied along the global Y axis (perpendicular to the beam,
in the horizontal plane). With the default 3D vertical-reference
convention this gives V2 / M3 — i.e. the "in-plane bending" pair.
**Distributed load (UDL) variant** — run the second case to see a
parabolic moment diagram:
```
Analyze → Cases → run "Uniform-Load"
Display → Show Force Diagram → M3 → parabolic, max 25 kN·m at fixed end
→ V2 → linear, max 10 kN at fixed end
```
### 2. Mode shapes — `portal_frame.osmodel` or `space_frame_3d.osmodel`
```
File → Open → space_frame_3d.osmodel
Analyze → Cases → run "Modal-6"
Display → Animate Mode Shape → mode 1 = X-sway, mode 2 = Y-sway
→ ▶ Play, scrub timeline, change scale
```
### 3. Time-history & hysteresis — `portal_frame.osmodel` or `space_frame_3d.osmodel`
```
File → Open → space_frame_3d.osmodel
Analyze → Cases → run "EQ-4s" (~5-10 sec on a modern laptop)
Display → Time-History Plot
- Node 12 (roof corner) + DOF 1 (X displacement) → "Add trace"
- Node 9 + DOF 1 → another trace, compare phase
Display → Hysteresis Plot
- X = Node 12 / DOF 1, Y = Node 12 / DOF 3 → orbit
```
### 4. Pushover — `sdof_pushover.osmodel`
```
File → Open → sdof_pushover.osmodel
Analyze → Cases → run "Push-X"
Display → Show Pushover Curve
→ linear segment from origin, then softens through yield
```
Note: this demo keeps the column elastic (proper nonlinear hinges require
BeamWithHingesElement with fibre sections — infrastructure is in place,
fibre-section editor is future work).
### 5. Nonlinear pushover with fiber hinges — `portal_pushover.osmodel`
```
File → Open → portal_pushover.osmodel
Analyze → Cases → run "Push-X"
Display → Show Pushover Curve
→ initial linear stiffness, then yield plateau as base hinges form
→ peak base shear corresponds to concrete crushing + rebar yield
```
The columns use BeamWithHingesElements with FiberSections (concrete core
+ rebar layers) wrapped in a SectionAggregator (torsion spring).
### 6. Gravity + time-history chain — `ex1a_canti2d_eq.osmodel`
```bash
File → Open → ex1a_canti2d_eq.osmodel
Analyze → Cases → run "Earthquake"
Display → Time-History Plot
- Node 2 + DOF 1 (Ux) → horizontal response of the cantilever tip
- Node 2 + DOF 2 (Uy) → verify gravity stays essentially locked
```
This model is intentionally tiny but important for workflow coverage:
it demonstrates the general transient recipe of
`Static preload → loadConst reset → UniformExcitation transient`
using a real ground-motion record imported into a `PathTimeSeries`.
### 7. Original OpenSees Ex 1a bundle — `ex1a_canti2d.osmodel`
```bash
File → Open → ex1a_canti2d.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the original cantilever-column Example 1a packaged as one model
with a shared gravity preload plus both lateral load variants. It is a
good small benchmark for checking that pushover and transient workflows
behave consistently on the same geometry.
### 8. Original OpenSees Ex 1b bundle — `ex1b_portal2d.osmodel`
```bash
File → Open → ex1b_portal2d.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the original elastic portal-frame Example 1b bundled as one
project. It is especially useful because the gravity preload is carried
by a distributed beam load instead of nodal loads only.
### 9. Variable-driven cantilever example — `ex2a_canti2d_elastic_element.osmodel`
```bash
File → Open → ex2a_canti2d_elastic_element.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the Ex2a cantilever tutorial recast as a project model. It is
useful when we want the same basic physics as Ex1a but with all major
dimensions and derived quantities exposed as named parameters.
### 10. Nonlinear aggregated-section cantilever — `ex2b_canti2d_inelastic_section.osmodel`
```bash
File → Open → ex2b_canti2d_inelastic_section.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the first nonlinear cantilever benchmark in the tutorial series.
It demonstrates how separate axial and flexural uniaxial responses can
be aggregated into one section and used by a force-based beam-column element.
### 11. Fiber-section cantilever example — `ex2c_canti2d_inelastic_fiber_section.osmodel`
```bash
File → Open → ex2c_canti2d_inelastic_fiber_section.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the Ex2c fiber-section counterpart to Ex2b. It is useful for
checking coupled axial-flexural section behavior with inelastic concrete
and steel materials assigned directly to fibers and rebar layers.
### 12. Example 3 build variants — `ex3_canti2d_*.osmodel`
```bash
File → Open → ex3_canti2d_elastic_element.osmodel
Analyze → Cases → run "Push" or "Earthquake"
```
The Example 3 family is useful when we want the same cantilever analyses
to run on three different build styles: elastic element, aggregated
uniaxial section, and fiber section, all with unit-scaled parameters.
### 13. Modal shear-building example — `eigen_two_storey_shear_frame.osmodel`
```bash
File → Open → eigen_two_storey_shear_frame.osmodel
Analyze → Cases → run "Modal-2"
Display → Animate Mode Shape
- mode 1 → in-phase storey sway
- mode 2 → out-of-phase storey sway
```
This example is useful for validating modal workflows on a tiny model
that still needs multi-point constraints (`equalDOF`) to behave like an
idealized shear frame.
### 9. Modal elastic frame example — `eigen_two_storey_one_bay_frame.osmodel`
```bash
File → Open → eigen_two_storey_one_bay_frame.osmodel
Analyze → Cases → run "Modal-2"
Display → Animate Mode Shape
- mode 1 → in-phase sway of the two storeys
- mode 2 → upper storey reverses relative to the first storey
```
This is the Chopra Example 10.5 frame counterpart to the shear-building
example above. It gives us a small modal benchmark with ordinary
beam-column frame behavior and no multi-point constraints.
### 13. Example 4 portal-frame variants
```bash
File -> Open -> ex4_portal2d_elastic_element.osmodel
Analyze -> Cases -> run "Push" or "Sine-Uniform"
Display -> Show Pushover Curve / Time-History Plot
```
The Example 4 family keeps the OpenSees split between model-building
and analysis files, but moves it into project variants. These are
useful benchmarks for pinned-base frame sway, distributed gravity on the
beam, and support-motion dynamics without depending on an external
earthquake file. The fiber-section transient is intentionally retained
as a strong nonlinear stress test and may stop early while still
producing useful partial histories.
## Regenerating the .osmodel files
If you change the Python scripts, run them to regenerate the saved models:
```bash
python examples/cantilever.py
python examples/portal_frame.py
python examples/space_frame_3d.py
python examples/sdof_pushover.py
python examples/portal_pushover.py
python examples/ex1a_canti2d.py
python examples/ex1b_portal2d.py
python examples/ex2a_canti2d_elastic_element.py
python examples/ex1a_canti2d_eq.py
python examples/ex2b_canti2d_inelastic_section.py
python examples/ex2c_canti2d_inelastic_fiber_section.py
python examples/ex3_canti2d_elastic_element.py
python examples/ex3_canti2d_inelastic_section.py
python examples/ex3_canti2d_inelastic_fiber_section.py
python examples/ex4_portal2d_elastic_element.py
python examples/ex4_portal2d_inelastic_section.py
python examples/ex4_portal2d_inelastic_fiber_section.py
python examples/eigen_two_storey_shear_frame.py
python examples/eigen_two_storey_one_bay_frame.py
```
Each script builds the project, saves it, reloads it, and asserts a clean
round-trip. The Python source is the source of truth; the `.osmodel` files
are generated artifacts checked in for convenience.

View file

@ -0,0 +1,460 @@
"""Shared builders for OpenSees Example 4 portal-frame variants."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
AggregatorDOF,
Concrete02,
ElasticBeamColumn,
ElasticSection,
ElasticUniaxial,
FiberSection,
ForceBeamColumn,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
RectangularPatch,
SectionAggregator,
StaticCase,
Steel01,
Steel02,
StraightLayer,
TransientCase,
UniformElementLoad,
UniformExcitationPattern,
UnitSystem,
)
INCH = 1.0
KIP = 1.0
SEC = 1.0
FT = 12.0 * INCH
KSI = KIP / INCH**2
PSI = KSI / 1000.0
G_ACCEL = 32.2 * FT / SEC**2
PI = math.pi
L_COL = 36.0 * FT
L_BEAM = 42.0 * FT
H_BEAM = 8.0 * FT
B_BEAM = 5.0 * FT
A_BEAM = B_BEAM * H_BEAM
IZ_BEAM = (1.0 / 12.0) * B_BEAM * H_BEAM**3
FC = -4.0 * KSI
E_C = 57.0 * KSI * math.sqrt(-FC / PSI)
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.1 * L_COL
PUSH_STEP = 0.001 * L_COL
NUM_INT_PTS = 5
SINE_AMPLITUDE = 0.5 * G_ACCEL
SINE_PERIOD = 0.35 * SEC
SINE_DURATION = 3.0 * SEC
GROUND_DT = 0.005 * SEC
ANALYSIS_DT = 0.01 * SEC
ANALYSIS_DURATION = 10.0 * SEC
ANALYSIS_STEPS = int(ANALYSIS_DURATION / ANALYSIS_DT)
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex4.Portal2D.analyze.Static.Push.tcl.txt"
REFERENCE_SINE_TCL = _ROOT / "data" / "Ex4.Portal2D.analyze.Dynamic.sine.Uniform.tcl.txt"
def sine_accel_values(
dt: float = GROUND_DT,
amplitude: float = SINE_AMPLITUDE,
period: float = SINE_PERIOD,
duration: float = SINE_DURATION,
) -> list[float]:
"""Sample the Example 4 sine support motion as a PathTimeSeries."""
omega = 2.0 * PI / period
n_points = int(round(duration / dt)) + 1
return [amplitude * math.sin(omega * i * dt) for i in range(n_points)]
def sine_velocity_values(
dt: float = GROUND_DT,
amplitude: float = SINE_AMPLITUDE,
period: float = SINE_PERIOD,
duration: float = SINE_DURATION,
) -> list[float]:
"""Velocity history consistent with the Tcl ``-vel0`` sine recipe."""
omega = 2.0 * PI / period
n_points = int(round(duration / dt)) + 1
return [-(amplitude / omega) * math.cos(omega * i * dt) for i in range(n_points)]
@dataclass(frozen=True)
class ElasticVariant:
name: str
description: str
weight: float
h_col: float
b_col: float
build_tcl_name: str
@dataclass(frozen=True)
class InelasticSectionVariant(ElasticVariant):
my_col: float
phi_y_col: float
hardening_ratio: float = 0.01
@dataclass(frozen=True)
class FiberVariant(ElasticVariant):
cover_col: float
num_bars_col: int
bar_area_col: float
eps1_u: float
eps2_u: float
lambda_: float
fy: float
es: float
bs: float
r0: float
cr1: float
cr2: float
n_fib_y: int
n_fib_z: int
ELASTIC_VARIANT = ElasticVariant(
name="OpenSees Ex 4 - Portal Frame (Elastic Build)",
description="Example 4 elastic portal frame with shared gravity, push, and sine-wave support motion.",
weight=4000.0 * KIP,
h_col=5.0 * FT,
b_col=4.0 * FT,
build_tcl_name="Ex4.Portal2D.build.ElasticElement.tcl.txt",
)
INELASTIC_SECTION_VARIANT = InelasticSectionVariant(
name="OpenSees Ex 4 - Portal Frame (Inelastic Section Build)",
description="Example 4 portal frame with aggregated uniaxial inelastic column sections and shared analyses.",
weight=4000.0 * KIP,
h_col=5.0 * FT,
b_col=4.0 * FT,
build_tcl_name="Ex4.Portal2D.build.InelasticSection.tcl.txt",
my_col=130000.0 * KIP * INCH,
phi_y_col=0.65e-4 / INCH,
)
FIBER_VARIANT = FiberVariant(
name="OpenSees Ex 4 - Portal Frame (Fiber Section Build)",
description="Example 4 portal frame with fiber-section columns and shared pushover / sine-wave analyses.",
weight=2000.0 * KIP,
h_col=5.0 * FT,
b_col=5.0 * FT,
build_tcl_name="Ex4.Portal2D.build.InelasticFiberSection.tcl.txt",
cover_col=6.0 * INCH,
num_bars_col=10,
bar_area_col=2.25 * INCH**2,
eps1_u=-0.003,
eps2_u=-0.05,
lambda_=0.1,
fy=66.8 * KSI,
es=29000.0 * KSI,
bs=0.01,
r0=18.0,
cr1=0.925,
cr2=0.15,
n_fib_y=16,
n_fib_z=4,
)
def _common_nodes(weight: float) -> list[Node]:
p_col = weight / 2.0
mass = p_col / G_ACCEL
return [
Node(id=1, name="Base-L", coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=2, name="Base-R", coords=(L_BEAM, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=3, name="Top-L", coords=(0.0, L_COL, 0.0), mass=(mass, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=4, name="Top-R", coords=(L_BEAM, L_COL, 0.0), mass=(mass, 0.0, 0.0, 0.0, 0.0, 0.0)),
]
def _common_patterns(weight: float) -> tuple[list[LinearTimeSeries | PathTimeSeries], list[PlainLoadPattern | UniformExcitationPattern]]:
p_col = weight / 2.0
w_beam = -weight / L_BEAM
time_series = [
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=200, name="Lateral"),
PathTimeSeries(
id=400,
name="Sine-0p5g",
dt=GROUND_DT,
values=sine_accel_values(),
file_path="generated:sine-wave",
),
PathTimeSeries(
id=401,
name="SineVel-0p5g",
dt=GROUND_DT,
values=sine_velocity_values(),
file_path="generated:sine-wave-velocity",
),
]
patterns = [
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
element_loads=[UniformElementLoad(element_id=3, wy=w_beam)],
),
PlainLoadPattern(
id=200,
name="Pushover-X",
time_series_id=200,
nodal_loads=[
NodalLoad(node_id=3, forces=(p_col, 0.0, 0.0, 0.0, 0.0, 0.0)),
NodalLoad(node_id=4, forces=(p_col, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
),
UniformExcitationPattern(
id=400,
name="Sine-Uniform-X",
direction=1,
accel_series_id=400,
vel_series_id=401,
),
]
return time_series, patterns
def _common_analyses() -> list[StaticCase | PushoverCase | TransientCase]:
return [
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=N_GRAVITY,
load_factor_increment=GRAVITY_STEP,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
PushoverCase(
id=2,
name="Push",
preload_case_ids=[1],
pattern_ids=[200],
control_node=3,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1, 2],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=6,
),
TransientCase(
id=3,
name="Sine-Uniform",
preload_case_ids=[1],
pattern_ids=[400],
dt=ANALYSIS_DT,
n_steps=ANALYSIS_STEPS,
system="BandGeneral",
constraints="Transformation",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="ModifiedNewton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
]
def build_ex4_portal2d_elastic_element() -> Project:
a_col = ELASTIC_VARIANT.b_col * ELASTIC_VARIANT.h_col
iz_col = (1.0 / 12.0) * ELASTIC_VARIANT.b_col * ELASTIC_VARIANT.h_col**3
time_series, patterns = _common_patterns(ELASTIC_VARIANT.weight)
return Project(
meta=ProjectMeta(
name=ELASTIC_VARIANT.name,
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=ELASTIC_VARIANT.description,
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=_common_nodes(ELASTIC_VARIANT.weight),
sections=[
ElasticSection(id=1, name="Columns", E=E_C, A=a_col, Iz=iz_col, Iy=iz_col, G=1.0, J=1.0),
ElasticSection(id=2, name="Beam", E=E_C, A=A_BEAM, Iz=IZ_BEAM, Iy=IZ_BEAM, G=1.0, J=1.0),
],
elements=[
ElasticBeamColumn(id=1, name="Col-L", nodes=(1, 3), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=2, name="Col-R", nodes=(2, 4), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=3, name="Beam", nodes=(3, 4), section_id=2, geom_transf="Linear"),
],
time_series=time_series,
load_patterns=patterns,
analyses=_common_analyses(),
)
def build_ex4_portal2d_inelastic_section() -> Project:
a_col = INELASTIC_SECTION_VARIANT.b_col * INELASTIC_SECTION_VARIANT.h_col
ei_col_cracked = INELASTIC_SECTION_VARIANT.my_col / INELASTIC_SECTION_VARIANT.phi_y_col
time_series, patterns = _common_patterns(INELASTIC_SECTION_VARIANT.weight)
return Project(
meta=ProjectMeta(
name=INELASTIC_SECTION_VARIANT.name,
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=INELASTIC_SECTION_VARIANT.description,
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=_common_nodes(INELASTIC_SECTION_VARIANT.weight),
materials=[
Steel01(
id=2,
name="Flexural-Steel01",
Fy=INELASTIC_SECTION_VARIANT.my_col,
E0=ei_col_cracked,
b=INELASTIC_SECTION_VARIANT.hardening_ratio,
),
ElasticUniaxial(id=3, name="Axial-Elastic", E=E_C * a_col),
],
sections=[
SectionAggregator(
id=1,
name="Column-Section",
pairings=[
AggregatorDOF(material_id=3, dof="P"),
AggregatorDOF(material_id=2, dof="Mz"),
],
),
ElasticSection(id=2, name="Beam", E=E_C, A=A_BEAM, Iz=IZ_BEAM, Iy=IZ_BEAM, G=1.0, J=1.0),
],
elements=[
ForceBeamColumn(id=1, name="Col-L", nodes=(1, 3), section_id=1, integration_points=NUM_INT_PTS, geom_transf="Linear"),
ForceBeamColumn(id=2, name="Col-R", nodes=(2, 4), section_id=1, integration_points=NUM_INT_PTS, geom_transf="Linear"),
ForceBeamColumn(id=3, name="Beam", nodes=(3, 4), section_id=2, integration_points=NUM_INT_PTS, geom_transf="Linear"),
],
time_series=time_series,
load_patterns=patterns,
analyses=_common_analyses(),
)
def build_ex4_portal2d_inelastic_fiber_section() -> Project:
cover_y = FIBER_VARIANT.h_col / 2.0
cover_z = FIBER_VARIANT.b_col / 2.0
core_y = cover_y - FIBER_VARIANT.cover_col
core_z = cover_z - FIBER_VARIANT.cover_col
ft_u = -0.14 * FC
ets = ft_u / 0.002
time_series, patterns = _common_patterns(FIBER_VARIANT.weight)
return Project(
meta=ProjectMeta(
name=FIBER_VARIANT.name,
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=FIBER_VARIANT.description,
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=_common_nodes(FIBER_VARIANT.weight),
materials=[
Concrete02(
id=1,
name="Cover-Concrete",
fpc=FC,
epsc0=FIBER_VARIANT.eps1_u,
fpcu=0.2 * FC,
epsU=FIBER_VARIANT.eps2_u,
lambda_=FIBER_VARIANT.lambda_,
ft=ft_u,
Ets=ets,
),
Steel02(
id=2,
name="Rebar-Steel02",
Fy=FIBER_VARIANT.fy,
E0=FIBER_VARIANT.es,
b=FIBER_VARIANT.bs,
R0=FIBER_VARIANT.r0,
cR1=FIBER_VARIANT.cr1,
cR2=FIBER_VARIANT.cr2,
),
],
sections=[
FiberSection(
id=1,
name="Column-Fiber-Section",
patches=[
RectangularPatch(
material_id=1,
n_fib_y=FIBER_VARIANT.n_fib_y,
n_fib_z=FIBER_VARIANT.n_fib_z,
y_i=-cover_y,
z_i=-cover_z,
y_j=cover_y,
z_j=cover_z,
),
],
layers=[
StraightLayer(
material_id=2,
n_bars=FIBER_VARIANT.num_bars_col,
bar_area=FIBER_VARIANT.bar_area_col,
y_start=-core_y,
z_start=core_z,
y_end=-core_y,
z_end=-core_z,
),
StraightLayer(
material_id=2,
n_bars=FIBER_VARIANT.num_bars_col,
bar_area=FIBER_VARIANT.bar_area_col,
y_start=core_y,
z_start=core_z,
y_end=core_y,
z_end=-core_z,
),
],
),
ElasticSection(id=2, name="Beam", E=E_C, A=A_BEAM, Iz=IZ_BEAM, Iy=IZ_BEAM, G=1.0, J=1.0),
],
elements=[
ForceBeamColumn(id=1, name="Col-L", nodes=(1, 3), section_id=1, integration_points=NUM_INT_PTS, geom_transf="Linear"),
ForceBeamColumn(id=2, name="Col-R", nodes=(2, 4), section_id=1, integration_points=NUM_INT_PTS, geom_transf="Linear"),
ForceBeamColumn(id=3, name="Beam", nodes=(3, 4), section_id=2, integration_points=NUM_INT_PTS, geom_transf="Linear"),
],
time_series=time_series,
load_patterns=patterns,
analyses=_common_analyses(),
)

View file

@ -0,0 +1,298 @@
{
"schema_version": 1,
"meta": {
"name": "Basic Truss",
"description": "3-bar asymmetric planar truss, linear static analysis.",
"author": "OpenSees Examples Manual - Example 1",
"units": "SI (m, N, kg, s, Pa)"
},
"ndm": 2,
"ndf": 2,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [
{
"id": "X1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X2",
"ordinate": 1.8288,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X3",
"ordinate": 3.6576,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X4",
"ordinate": 4.2672,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"y_grid_lines": [
{
"id": "Y1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y2",
"ordinate": 2.4383999999999997,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"z_grid_lines": [
{
"id": "Z1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base-L",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
false
]
},
{
"id": 2,
"name": "Base-M",
"coords": [
3.6576,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
false
]
},
{
"id": 3,
"name": "Base-R",
"coords": [
4.2672,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "Crown",
"coords": [
1.8288,
2.4383999999999997,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "Steel-3000ksi",
"type": "Elastic",
"E": 20685000000.0,
"eta": 0.0,
"Eneg": null
}
],
"sections": [],
"elements": [
{
"id": 1,
"name": "Bar-1-4",
"type": "Truss",
"nodes": [
1,
4
],
"area": 0.0064516,
"material_id": 1,
"rho": 0.0
},
{
"id": 2,
"name": "Bar-2-4",
"type": "Truss",
"nodes": [
2,
4
],
"area": 0.0032258,
"material_id": 1,
"rho": 0.0
},
{
"id": 3,
"name": "Bar-3-4",
"type": "Truss",
"nodes": [
3,
4
],
"area": 0.0032258,
"material_id": 1,
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Ramp",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "Tip Load",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 4,
"forces": [
444822.16,
-222411.08,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Static",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 1,
"load_factor_increment": 1.0,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "LoadControl",
"algorithm": "Linear",
"test": "NormDispIncr",
"tolerance": 1e-08,
"max_iter": 25
}
]
}

163
examples/basic_truss.py Normal file
View file

@ -0,0 +1,163 @@
"""Basic Truss Example — OpenSees Examples Manual, Example 1 (exact geometry).
Three-bar pin-jointed truss in 2D under a nodal load at the crown.
Geometry as in the OpenSees Tcl script (units: kip, in)::
node 1 0.0 0.0 node 4 72.0 96.0
node 2 144.0 0.0 (crown — loaded here)
node 3 168.0 0.0
─── Note the asymmetry:
Base nodes: node 2 at x=144"
(0, 0) ───────── (144, 0) ── (168, 0) node 3 at x=168"
↑ ↑ (only 24" apart!)
This is an asymmetric three-bar truss reaching to a single crown
joint (node 4) at the top. The three bars have different lengths
and two different areas (bar 1 = 10 in², bars 2 and 3 = 5 in²).
Loads (at node 4): Fx = +100 kip, Fy = -50 kip.
Material: Elastic, E = 3000 ksi.
Analytical solution in kip-in units (verified by OpenSees Tcl run):
u_x(node 4) ≈ +0.530 in
u_y(node 4) ≈ -0.178 in
In OTKO we always work in SI internally (m, N), so we
convert inches → metres (×0.0254), kips → newtons (×4448.22) and
ksi → pascals (×6.895e6). The converted model gives the same
dimensionless solution when you multiply back by the inverse
conversion.
Produces ``examples/basic_truss.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
CoordinateGridSystem,
ElasticUniaxial,
GridSystem,
LinearTimeSeries,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
StaticCase,
TrussElement,
UnitSystem,
make_grid_lines,
)
from otko.services import load_project, save_project
# Unit conversions (imperial → SI).
IN_TO_M = 0.0254
KIP_TO_N = 4448.2216
KSI_TO_PA = 6.895e6
def build_basic_truss() -> Project:
"""Three-bar planar truss — Example 1 of the OpenSees user manual.
Geometry in inches → metres; material E in ksi → Pa; areas in
in² → m²; loads in kips → N. The SI model is a dimensionless
scale of the original kip-in model, so the OpenSees solve gives
the same shape / ratio of displacements.
"""
# Node coordinates in inches (from Tcl) — converted to metres.
x1, y1 = 0.0, 0.0
x2, y2 = 144.0, 0.0
x3, y3 = 168.0, 0.0
x4, y4 = 72.0, 96.0
# Areas in in² — converted to m².
a1 = 10.0 * IN_TO_M ** 2
a2 = 5.0 * IN_TO_M ** 2
a3 = 5.0 * IN_TO_M ** 2
# Loads in kips — converted to N.
fx = 100.0 * KIP_TO_N
fy = -50.0 * KIP_TO_N
# Material E in ksi → Pa.
e = 3000.0 * KSI_TO_PA
# Grid that covers all four nodes (for the canvas overlay).
x_ords = [x1 * IN_TO_M, x4 * IN_TO_M, x2 * IN_TO_M, x3 * IN_TO_M]
y_ords = [y1 * IN_TO_M, y4 * IN_TO_M]
return Project(
meta=ProjectMeta(
name="Basic Truss",
author="OpenSees Examples Manual - Example 1",
description="3-bar asymmetric planar truss, linear static analysis.",
units=UnitSystem.SI_M_N,
),
ndm=2, ndf=2,
coord_systems=[
CoordinateGridSystem(
name="Global",
grid=GridSystem(
x_grid_lines=make_grid_lines("X", x_ords),
y_grid_lines=make_grid_lines("Y", y_ords),
z_grid_lines=make_grid_lines("Z", [0.0]),
),
),
],
nodes=[
Node(id=1, name="Base-L", coords=(x1 * IN_TO_M, y1 * IN_TO_M, 0.0),
restraint=(True, True, False, False, False, False)),
Node(id=2, name="Base-M", coords=(x2 * IN_TO_M, y2 * IN_TO_M, 0.0),
restraint=(True, True, False, False, False, False)),
Node(id=3, name="Base-R", coords=(x3 * IN_TO_M, y3 * IN_TO_M, 0.0),
restraint=(True, True, False, False, False, False)),
Node(id=4, name="Crown", coords=(x4 * IN_TO_M, y4 * IN_TO_M, 0.0)),
],
materials=[
ElasticUniaxial(id=1, name="Steel-3000ksi", E=e),
],
sections=[],
elements=[
TrussElement(id=1, name="Bar-1-4", nodes=(1, 4),
area=a1, material_id=1),
TrussElement(id=2, name="Bar-2-4", nodes=(2, 4),
area=a2, material_id=1),
TrussElement(id=3, name="Bar-3-4", nodes=(3, 4),
area=a3, material_id=1),
],
time_series=[LinearTimeSeries(id=1, name="Ramp")],
load_patterns=[
PlainLoadPattern(
id=1, name="Tip Load",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=4, forces=(fx, fy, 0, 0, 0, 0)),
],
),
],
analyses=[
StaticCase(id=1, name="Static", pattern_ids=[1], n_steps=1),
],
)
def main() -> None:
project = build_basic_truss()
project.validate_references()
print(f"Built '{project.meta.name}' - {len(project.nodes)} nodes, "
f"{len(project.elements)} truss bars, {len(project.analyses)} case.")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

200
examples/beam_quad_2d.py Normal file
View file

@ -0,0 +1,200 @@
"""Simply Supported Beam with 2D Solid (Quad) Elements — OpenSees Ex 6.4.
A 40" × 10" simply supported deep beam meshed with 16×4 plane-stress
quadrilateral elements under two centre-span vertical loads (one at the
top midspan, one at the bottom midspan, each 1 kip downward). The
load is ramped up over 10 LoadControl steps, giving a final midspan
deflection of ~0.394 in. A second case then *removes* the preload and
lets the beam free-vibrate with 2% stiffness-proportional damping for
1500 Newmark steps at dt = 0.5 s — reproducing the full Tcl flow.
Matches the Tcl walkthrough at:
https://opensees.berkeley.edu/wiki/index.php?title=Simply_supported_beam_modeled_with_two_dimensional_solid_elements
Model (kip, in, sec):
- Geometry: L = 40 in, H = 10 in, t = 1 in (plane stress)
- Material: ElasticIsotropic — E = 1000 ksi, ν = 0.25, ρ = 3.0
- Elements: 16 × 4 = 64 four-node quads (``quad`` variant,
``PlaneStress2D`` formulation)
- Supports: pin at node 1 (bottom-left), roller at node 17
(bottom-right, Uy restrained only)
- Loads: 2 × 1 kip ↓ at (20, 0) and (20, 10), ramped via
LoadControl from 0 to 10 over 10 steps
Analysis cases:
1. ``Static`` — 10-step LoadControl, midspan Uy ≈ -0.394 in
2. ``FreeVibration`` — Transient with preload = case 1, remove
pattern 1, Newmark (γ=0.5, β=0.25), dt = 0.5 s, 1500 steps,
βK auto-computed from 2% damping at the 1st mode.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
ElasticIsotropic,
LinearTimeSeries,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
QuadElement,
StaticCase,
TransientCase,
UnitSystem,
)
from otko.services import load_project, save_project
# Geometry (inches).
L = 40.0
H = 10.0
THICKNESS = 1.0
# Mesh — nx must be even so a node lands at the midspan.
NX = 16
NY = 4
# Material (kip-in units).
E = 1000.0
NU = 0.25
RHO = 3.0
def _node_id(i: int, j: int) -> int:
"""Row-major node id (matching the Tcl ``block2D`` numbering).
i = 0..NX column index (along length), j = 0..NY row index (through depth).
"""
return j * (NX + 1) + i + 1
def _mid_bottom_node_id() -> int:
"""Node at (L/2, 0) — bottom flange midspan where one load attaches."""
return _node_id(NX // 2, 0)
def _mid_top_node_id() -> int:
"""Node at (L/2, H) — top flange midspan where the other load attaches."""
return _node_id(NX // 2, NY)
def _right_bottom_node_id() -> int:
"""Bottom-right corner — the roller support."""
return _node_id(NX, 0)
def build_beam_quad_2d() -> Project:
dx = L / NX
dy = H / NY
nodes: list[Node] = []
for j in range(NY + 1):
for i in range(NX + 1):
nid = _node_id(i, j)
if nid == 1:
restraint = (True, True, False, False, False, False) # pin
elif nid == _right_bottom_node_id():
restraint = (False, True, False, False, False, False) # roller (Uy only)
else:
restraint = (False,) * 6
nodes.append(Node(
id=nid, name=f"N{nid}",
coords=(i * dx, j * dy, 0.0),
restraint=restraint,
))
elements: list[QuadElement] = []
eid = 1
for j in range(NY):
for i in range(NX):
n1 = _node_id(i, j) # bottom-left
n2 = _node_id(i + 1, j) # bottom-right
n3 = _node_id(i + 1, j + 1) # top-right
n4 = _node_id(i, j + 1) # top-left (counter-clockwise)
elements.append(QuadElement(
id=eid, name=f"Q{eid}",
nodes=(n1, n2, n3, n4),
thickness=THICKNESS,
material_id=1,
variant="quad",
behaviour="PlaneStress2D",
))
eid += 1
return Project(
meta=ProjectMeta(
name="Simply Supported Beam — Quad Elements (OpenSees Ex 6.4)",
author="OpenSees Examples Manual",
description=(
f"{NX}×{NY} plane-stress quad mesh of a {L:.0f}×{H:.0f} deep "
"beam with two midspan point loads, LoadControl 0→10 in 10 steps."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2, ndf=2,
nodes=nodes,
materials=[ElasticIsotropic(id=1, name="Elastic", E=E, nu=NU, rho=RHO)],
elements=elements,
time_series=[LinearTimeSeries(id=1, name="Ramp")],
load_patterns=[PlainLoadPattern(
id=1, name="MidspanLoad",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=_mid_bottom_node_id(),
forces=(0.0, -1.0, 0.0, 0.0, 0.0, 0.0)),
NodalLoad(node_id=_mid_top_node_id(),
forces=(0.0, -1.0, 0.0, 0.0, 0.0, 0.0)),
],
)],
analyses=[
StaticCase(
id=1, name="Static",
pattern_ids=[1],
n_steps=10, load_factor_increment=1.0,
system="ProfileSPD", constraints="Plain",
integrator="LoadControl", algorithm="Newton",
test="EnergyIncr", tolerance=1e-12, max_iter=10,
),
# Free-vibration continuation: runs case 1 to completion,
# drops the midspan load pattern, sets up 2% βK Rayleigh
# damping from the 1st mode, then integrates 1500 steps of
# Newmark (γ=0.5, β=0.25) at dt = 0.5 s.
TransientCase(
id=2, name="FreeVibration",
preload_case_ids=[1],
remove_patterns=[1],
pattern_ids=[],
dt=0.5, n_steps=1500,
system="BandGeneral", constraints="Plain",
integrator="Newmark", integrator_params=(0.5, 0.25),
algorithm="Newton",
test="EnergyIncr", tolerance=1e-12, max_iter=10,
rayleigh_mode1_damping=0.02,
),
],
)
def main() -> None:
project = build_beam_quad_2d()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" ndm={project.ndm}, ndf={project.ndf}, "
f"units={project.meta.units.value}")
print(f" {len(project.nodes)} nodes, {len(project.elements)} quads")
print(f" Midspan loaded nodes: bottom={_mid_bottom_node_id()}, "
f"top={_mid_top_node_id()}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

379
examples/cantilever.osmodel Normal file
View file

@ -0,0 +1,379 @@
{
"schema_version": 1,
"meta": {
"name": "Cantilever",
"description": "",
"author": "Ozan",
"units": "SI (m, N, kg, s, Pa)"
},
"ndm": 3,
"ndf": 6,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "N1",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
true,
true,
true
]
},
{
"id": 2,
"name": "N2",
"coords": [
1.0,
0.0,
0.0
],
"mass": [
1000.0,
1000.0,
1000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 3,
"name": "N3",
"coords": [
2.0,
0.0,
0.0
],
"mass": [
1000.0,
1000.0,
1000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "N4",
"coords": [
3.0,
0.0,
0.0
],
"mass": [
1000.0,
1000.0,
1000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 5,
"name": "N5",
"coords": [
4.0,
0.0,
0.0
],
"mass": [
1000.0,
1000.0,
1000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 6,
"name": "N6",
"coords": [
5.0,
0.0,
0.0
],
"mass": [
1000.0,
1000.0,
1000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [],
"sections": [
{
"id": 1,
"name": "W12x40",
"type": "ElasticSection",
"E": 200000000000.0,
"A": 0.0076,
"Iz": 0.0002,
"Iy": 4.5e-05,
"G": 80000000000.0,
"J": 8.5e-07
}
],
"elements": [
{
"id": 1,
"name": "E1",
"type": "ElasticBeamColumn",
"nodes": [
1,
2
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 2,
"name": "E2",
"type": "ElasticBeamColumn",
"nodes": [
2,
3
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 3,
"name": "E3",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 4,
"name": "E4",
"type": "ElasticBeamColumn",
"nodes": [
4,
5
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 5,
"name": "E5",
"type": "ElasticBeamColumn",
"nodes": [
5,
6
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Ramp",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "TipLoad",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 6,
"forces": [
0.0,
-10000.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
},
{
"id": 2,
"name": "UniformLoad",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [],
"element_loads": [
{
"element_id": 1,
"wy": -2000.0,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 2,
"wy": -2000.0,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 3,
"wy": -2000.0,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 4,
"wy": -2000.0,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 5,
"wy": -2000.0,
"wz": 0.0,
"wx": 0.0
}
]
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Tip-Load",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 1,
"load_factor_increment": 1.0,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "LoadControl",
"algorithm": "Linear",
"test": "NormDispIncr",
"tolerance": 1e-08,
"max_iter": 25
},
{
"id": 2,
"name": "Uniform-Load",
"type": "Static",
"pattern_ids": [
2
],
"n_steps": 1,
"load_factor_increment": 1.0,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "LoadControl",
"algorithm": "Linear",
"test": "NormDispIncr",
"tolerance": 1e-08,
"max_iter": 25
},
{
"id": 3,
"name": "Modal-3",
"type": "Modal",
"n_modes": 3,
"solver": "genBandArpack"
}
]
}

121
examples/cantilever.py Normal file
View file

@ -0,0 +1,121 @@
"""Cantilever beam example — ideal for showing force diagrams.
A 5 m horizontal cantilever fixed at the left end, with a point load
at the tip and a distributed-equivalent set of nodal loads along the
span. Pure bending response makes for instantly recognisable N/V/M
diagrams.
Run from the repository root:
python examples/cantilever.py
Produces ``examples/cantilever.osmodel``.
Open in the GUI, run "Tip-Load" Static case, then:
Display → Show Force Diagram → V2 (shear) → constant within each segment
Display → Show Force Diagram → M3 (moment) → linear, max at the fixed end
Display → Show Deformed Shape → classic cantilever curve
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
ModalCase,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
StaticCase,
UniformElementLoad,
UnitSystem,
)
from otko.services import load_project, save_project
def build_cantilever() -> Project:
"""5 m cantilever discretised into 5 elements (1 m each)."""
n_segments = 5
seg_len = 1.0
nodes = []
for i in range(n_segments + 1):
x = i * seg_len
restraint = (True,) * 6 if i == 0 else (False,) * 6
# Lump some mass at every free node so modal works too.
mass = (1000.0, 1000.0, 1000.0, 0.0, 0.0, 0.0) if i > 0 else (0.0,) * 6
nodes.append(Node(id=i + 1, name=f"N{i+1}",
coords=(x, 0.0, 0.0), restraint=restraint, mass=mass))
elements = [
ElasticBeamColumn(id=i + 1, name=f"E{i+1}",
nodes=(i + 1, i + 2), section_id=1)
for i in range(n_segments)
]
return Project(
meta=ProjectMeta(name="Cantilever", author="Ozan", units=UnitSystem.SI_M_N),
ndm=3, ndf=6,
nodes=nodes,
sections=[
ElasticSection(
id=1, name="W12x40",
E=200e9, A=0.0076,
Iz=2.0e-4, Iy=4.5e-5,
G=80e9, J=8.5e-7,
),
],
elements=elements,
time_series=[LinearTimeSeries(id=1, name="Ramp")],
load_patterns=[
# Tip horizontal load (y) of 10 kN — this produces shear V2
# and moment M3 about the strong axis, the conventional 2D
# "in-plane bending" components.
PlainLoadPattern(
id=1, name="TipLoad",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=n_segments + 1,
forces=(0.0, -10_000.0, 0.0, 0, 0, 0)),
],
),
# Uniform distributed load wy = -2 kN/m along every element.
# Produces parabolic moment diagram, max at fixed end
# (M_max = q·L²/2 = 2·5²/2 = 25 kN·m).
PlainLoadPattern(
id=2, name="UniformLoad",
time_series_id=1,
element_loads=[
UniformElementLoad(element_id=i + 1, wy=-2_000.0)
for i in range(n_segments)
],
),
],
analyses=[
StaticCase(id=1, name="Tip-Load", pattern_ids=[1]),
StaticCase(id=2, name="Uniform-Load", pattern_ids=[2]),
ModalCase(id=3, name="Modal-3", n_modes=3),
],
)
def main() -> None:
project = build_cantilever()
project.validate_references()
print(f"Built '{project.meta.name}' — {len(project.nodes)} nodes, "
f"{len(project.elements)} elements, {len(project.analyses)} cases.")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,275 @@
{
"schema_version": 1,
"meta": {
"name": "Concrete04 Cantilever",
"description": "Single RC column cantilever using Concrete04 (Popovics) fiber section. Demonstrates the Concrete04 material end-to-end: schema → model → runner → results.",
"author": "",
"units": "SI (m, N, kg, s, Pa)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
false,
false,
false
]
},
{
"id": 2,
"name": "Top",
"coords": [
0.0,
3.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "C30-Popovics",
"type": "Concrete04",
"fpc": -30000000.0,
"epsc0": -0.002,
"epscu": -0.005,
"Ec": 25742960202.74281,
"fct": 2200000.0,
"et": 0.0001,
"beta": null
},
{
"id": 2,
"name": "Rebar-B500",
"type": "Steel02",
"Fy": 420000000.0,
"E0": 200000000000.0,
"b": 0.01,
"R0": 18.0,
"cR1": 0.925,
"cR2": 0.15
}
],
"sections": [
{
"id": 1,
"name": "RC-Fiber-C04",
"type": "FiberSection",
"GJ": null,
"patches": [
{
"kind": "rect",
"material_id": 1,
"n_fib_y": 8,
"n_fib_z": 4,
"y_i": -0.15,
"z_i": -0.15,
"y_j": 0.15,
"z_j": 0.15
}
],
"layers": [
{
"kind": "straight",
"material_id": 2,
"n_bars": 4,
"bar_area": 0.000314,
"y_start": -0.12,
"z_start": 0.12,
"y_end": -0.12,
"z_end": -0.12
},
{
"kind": "straight",
"material_id": 2,
"n_bars": 4,
"bar_area": 0.000314,
"y_start": 0.12,
"z_start": 0.12,
"y_end": 0.12,
"z_end": -0.12
}
],
"fibres": []
}
],
"elements": [
{
"id": 1,
"name": "Column",
"type": "ForceBeamColumn",
"nodes": [
1,
2
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
}
],
"mp_constraints": [],
"time_series": [
{
"id": 1,
"name": "Gravity",
"type": "Linear",
"factor": 1.0
},
{
"id": 2,
"name": "Lateral",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "Gravity",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 2,
"forces": [
0.0,
-300000.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
},
{
"id": 2,
"name": "Pushover-X",
"type": "Plain",
"time_series_id": 2,
"nodal_loads": [
{
"node_id": 2,
"forces": [
50000.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Gravity",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 10,
"load_factor_increment": 0.1,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "LoadControl",
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-08,
"max_iter": 10
},
{
"id": 2,
"name": "Push-X",
"type": "Pushover",
"pattern_ids": [
2
],
"control_node": 2,
"control_dof": 1,
"target_disp": 0.15000000000000002,
"step_size": 0.003,
"base_nodes": [
1
],
"system": "BandGeneral",
"constraints": "Plain",
"algorithm": "Newton",
"test": "EnergyIncr",
"tolerance": 1e-08,
"max_iter": 10,
"preload_case_ids": [
1
]
}
]
}

View file

@ -0,0 +1,215 @@
"""Concrete04 (Popovics) fiber-section cantilever.
A single reinforced-concrete column cantilever using Concrete04 as the
fiber material, mirroring the ex2c_canti2d_inelastic_fiber_section example
but with Popovics concrete in place of Kent-Scott-Park (Concrete02).
Run from the repository root:
python examples/concrete04_cantilever.py
Produces ``examples/concrete04_cantilever.osmodel``.
"""
from __future__ import annotations
import math
import sys
from pathlib import Path
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
Concrete04,
FiberSection,
ForceBeamColumn,
LinearTimeSeries,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
RectangularPatch,
StaticCase,
Steel02,
StraightLayer,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
# ── Section geometry (SI units: m, N, Pa) ─────────────────────────────────────
L_COL = 3.0 # column height [m]
B_COL = 0.30 # section width [m]
H_COL = 0.30 # section depth [m]
COVER = 0.03 # clear cover [m]
# ── Material parameters (SI) ───────────────────────────────────────────────────
FC = -30e6 # peak compressive strength [Pa] (negative)
EPSC0 = -0.002 # strain at peak
EPSCU = -0.005 # ultimate compressive strain
# Initial tangent: Ec = 4700 * sqrt(|fc| / 1e6) MPa (ACI 318 formula, SI)
EC = 4700.0 * math.sqrt(abs(FC) / 1e6) * 1e6 # ≈ 25.74 GPa
FCT = 2.2e6 # tensile strength [Pa]
ET = 1e-4 # ultimate tensile strain
FY = 420e6 # rebar yield stress [Pa]
ES = 200e9 # rebar elastic modulus [Pa]
BS = 0.01 # strain-hardening ratio
NUM_INT_PTS = 5
N_BARS = 4
BAR_AREA = 314e-6 # m² (≈ 20 mm diameter rebar)
P_GRAVITY = -300e3 # gravity axial load [N] (negative = compressive)
H_LOAD = 50e3 # lateral load at tip [N]
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.05 * L_COL # [m]
PUSH_STEP = 0.001 * L_COL # [m]
def build_concrete04_cantilever() -> Project:
core_y = H_COL / 2.0 - COVER
core_z = B_COL / 2.0 - COVER
return Project(
meta=ProjectMeta(
name="Concrete04 Cantilever",
description=(
"Single RC column cantilever using Concrete04 (Popovics) "
"fiber section. Demonstrates the Concrete04 material "
"end-to-end: schema → model → runner → results."
),
units=UnitSystem.SI_M_N,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1, name="Base",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, True, False, False, False),
),
Node(id=2, name="Top", coords=(0.0, L_COL, 0.0)),
],
materials=[
Concrete04(
id=1, name="C30-Popovics",
fpc=FC, epsc0=EPSC0, epscu=EPSCU, Ec=EC,
fct=FCT, et=ET,
),
Steel02(
id=2, name="Rebar-B500",
Fy=FY, E0=ES, b=BS,
),
],
sections=[
FiberSection(
id=1, name="RC-Fiber-C04",
patches=[
RectangularPatch(
material_id=1,
n_fib_y=8, n_fib_z=4,
y_i=-H_COL / 2, z_i=-B_COL / 2,
y_j= H_COL / 2, z_j= B_COL / 2,
),
],
layers=[
StraightLayer(
material_id=2,
n_bars=N_BARS, bar_area=BAR_AREA,
y_start=-core_y, z_start= core_z,
y_end =-core_y, z_end =-core_z,
),
StraightLayer(
material_id=2,
n_bars=N_BARS, bar_area=BAR_AREA,
y_start= core_y, z_start= core_z,
y_end = core_y, z_end =-core_z,
),
],
),
],
elements=[
ForceBeamColumn(
id=1, name="Column",
nodes=(1, 2),
section_id=1,
integration_points=NUM_INT_PTS,
geom_transf="Linear",
),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
],
load_patterns=[
PlainLoadPattern(
id=1, name="Gravity", time_series_id=1,
nodal_loads=[
NodalLoad(node_id=2, forces=(0.0, P_GRAVITY, 0.0, 0.0, 0.0, 0.0)),
],
),
PlainLoadPattern(
id=2, name="Pushover-X", time_series_id=2,
nodal_loads=[
NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
),
],
analyses=[
StaticCase(
id=1, name="Gravity",
pattern_ids=[1],
n_steps=N_GRAVITY,
load_factor_increment=GRAVITY_STEP,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=10,
),
PushoverCase(
id=2, name="Push-X",
preload_case_ids=[1],
pattern_ids=[2],
control_node=2,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=10,
),
],
)
def main() -> None:
project = build_concrete04_cantilever()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" L={L_COL} m, BxH={B_COL}x{H_COL} m, Ec={EC/1e9:.2f} GPa")
print(f" Gravity + pushover cases: {len(project.analyses)}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

1598
examples/data/A10000.txt Normal file

File diff suppressed because it is too large Load diff

800
examples/data/BM68elc.acc Normal file
View file

@ -0,0 +1,800 @@
-.1368849E-02 -.1659410E-02 -.1466880E-02 -.6865326E-03 -.6491235E-03
-.6172128E-03 -.5942289E-03 -.5720329E-03 -.5517003E-03 -.5367939E-03
-.5300330E-03 -.5315104E-03 -.5389920E-03 -.5492582E-03 -.5592027E-03
-.5659958E-03 -.5672101E-03 -.5617805E-03 -.5502959E-03 -.5347288E-03
-.5176619E-03 -.5013709E-03 -.4873454E-03 -.4763228E-03 -.4683559E-03
-.4626830E-03 -.4579708E-03 -.4512405E-03 -.4376077E-03 -.4130071E-03
-.3772566E-03 -.3363394E-03 -.3030926E-03 -.2926074E-03 -.3144186E-03
-.3668375E-03 -.4373818E-03 -.5104884E-03 -.5745380E-03 -.6248976E-03
-.6621411E-03 -.6878470E-03 -.7014600E-03 -.6985488E-03 -.6737667E-03
-.6258232E-03 -.5616336E-03 -.4955459E-03 -.4432164E-03 -.4144737E-03
-.4103149E-03 -.4251781E-03 -.4493034E-03 -.4742715E-03 -.4942019E-03
-.5021476E-03 -.4907415E-03 -.4549906E-03 -.4008473E-03 -.3457893E-03
-.3076823E-03 -.2975411E-03 -.3148410E-03 -.3469618E-03 -.3746677E-03
-.3814194E-03 -.3598713E-03 -.3127679E-03 -.2503611E-03 -.1870004E-03
-.1381307E-03 -.1166398E-03 -.1296596E-03 -.1730662E-03 -.2365307E-03
-.3092055E-03 -.3795541E-03 -.4410602E-03 -.4886626E-03 -.5170590E-03
-.5252823E-03 -.5139311E-03 -.4858098E-03 -.4459504E-03 -.3992677E-03
-.3528575E-03 -.3119584E-03 -.2767747E-03 -.2493392E-03 -.2343354E-03
-.2328132E-03 -.2443271E-03 -.2710242E-03 -.3142711E-03 -.3756243E-03
-.4585760E-03 -.5641321E-03 -.6901264E-03 -.8277058E-03 -.9398496E-03
-.9604577E-03 -.8289034E-03 -.5332316E-03 -.1380318E-03 .2342108E-03
.4651973E-03 .5026886E-03 .3746881E-03 .1623773E-03 -.5159882E-04
-.2224880E-03 -.3475301E-03 -.4529523E-03 -.5485990E-03 -.5963545E-03
-.5423656E-03 -.3657646E-03 -.1048402E-03 .1563278E-03 .3291475E-03
.3576328E-03 .2409380E-03 .2377646E-04 -.2316319E-03 -.4694656E-03
-.6625028E-03 -.8119362E-03 -.9298628E-03 -.1028468E-02 -.1108383E-02
-.1131224E-02 -.1020689E-02 -.7165112E-03 -.2327270E-03 .3181869E-03
.7732246E-03 .1012043E-02 .1013671E-02 .8560355E-03 .6690806E-03
.5646968E-03 .5919845E-03 .7377345E-03 .9490117E-03 .1157766E-02
.1288701E-02 .1282487E-02 .1122759E-02 .8318915E-03 .4619941E-03
.7578448E-04 -.2595554E-03 -.4939353E-03 -.6127490E-03 -.6275750E-03
-.5752344E-03 -.4995743E-03 -.4264229E-03 -.3601404E-03 -.3004083E-03
-.2406227E-03 -.1628483E-03 -.3998632E-04 .1649808E-03 .4539122E-03
.7517190E-03 .9316287E-03 .8846478E-03 .5857181E-03 .1074495E-03
-.4397852E-03 -.9508948E-03 -.1343749E-02 -.1561289E-02 -.1537678E-02
-.1227062E-02 -.6359309E-03 .1519731E-03 .9751270E-03 .1659683E-02
.2090323E-02 .2247826E-02 .2201305E-02 .2058203E-02 .1895573E-02
.1737760E-02 .1553215E-02 .1242380E-02 .7156433E-03 .5001116E-04
-.5054628E-03 -.6890887E-03 -.4238027E-03 .1011130E-03 .5498104E-03
.6308834E-03 .2434936E-03 -.5144872E-03 -.1425978E-02 -.2207871E-02
-.2588403E-02 -.2416130E-02 -.1701441E-02 -.6324158E-03 .4489725E-03
.1181227E-02 .1316547E-02 .7973359E-03 -.2602228E-03 -.1657394E-02
-.3151829E-02 -.4439463E-02 -.5178411E-02 -.5151548E-02 -.4419849E-02
-.3294586E-02 -.2165649E-02 -.1266916E-02 -.5719502E-03 .1140084E-03
.9530592E-03 .1899989E-02 .2754509E-02 .3333837E-02 .3558579E-02
.3481950E-02 .3221851E-02 .2824910E-02 .2273374E-02 .1577169E-02
.8367896E-03 .2027145E-03 -.2373641E-03 -.4614556E-03 -.4731789E-03
-.2183073E-03 .4104828E-03 .1459868E-02 .2860114E-02 .4348360E-02
.5528933E-02 .6097511E-02 .5985376E-02 .5383175E-02 .4639648E-02
.4059716E-02 .3743902E-02 .3490429E-02 .2858654E-02 .1525807E-02
-.3418321E-03 -.2117817E-02 -.3132606E-02 -.3067405E-02 -.2029459E-02
-.4221254E-03 .1279269E-02 .2716792E-02 .3687018E-02 .4125139E-02
.4074137E-02 .3662913E-02 .3043893E-02 .2233206E-02 .1065048E-02
-.5910665E-03 -.2497506E-02 -.4059191E-02 -.4734788E-02 -.4355504E-02
-.3183642E-02 -.1697557E-02 -.2707831E-03 .9875352E-03 .2151907E-02
.3263816E-02 .4199429E-02 .4759887E-02 .4825527E-02 .4432848E-02
.3760105E-02 .3017075E-02 .2350750E-02 .1810708E-02 .1373070E-02
.9716151E-03 .5537977E-03 .1551401E-03 -.1146315E-03 -.1525016E-03
.8037190E-04 .5542061E-03 .1173618E-02 .1820585E-02 .2425000E-02
.2926893E-02 .3238843E-02 .3279656E-02 .2992806E-02 .2362163E-02
.1372295E-02 .2313812E-04 -.1448616E-02 -.2543549E-02 -.2850379E-02
-.2427525E-02 -.1864917E-02 -.1841201E-02 -.2558433E-02 -.3556204E-02
-.4063939E-02 -.3529889E-02 -.1969941E-02 .1971637E-05 .1588542E-02
.2244687E-02 .1873185E-02 .7977154E-03 -.5171107E-03 -.1717952E-02
-.2648191E-02 -.3320288E-02 -.3766790E-02 -.3906620E-02 -.3602483E-02
-.2795473E-02 -.1624758E-02 -.4698606E-03 .2144823E-03 .1413113E-03
-.6730203E-03 -.1847835E-02 -.2630158E-02 -.2274249E-02 -.6524350E-03
.1573817E-02 .3385189E-02 .4056466E-02 .3445002E-02 .1897480E-02
-.7834726E-06 -.1758264E-02 -.3163954E-02 -.4224580E-02 -.4961730E-02
-.5277184E-02 -.4996010E-02 -.4023828E-02 -.2450240E-02 -.5552267E-03
.1274664E-02 .2708539E-02 .3615917E-02 .4076754E-02 .4278436E-02
.4381952E-02 .4374117E-02 .4039416E-02 .3125690E-02 .1526682E-02
-.6629957E-03 -.3174034E-02 -.5620508E-02 -.7393678E-02 -.7657670E-02
-.5879189E-02 -.2309056E-02 .2104524E-02 .6124863E-02 .8661836E-02
.9093149E-02 .7418333E-02 .4183904E-02 .1723107E-03 -.3926354E-02
-.7599501E-02 -.1040893E-01 -.1196732E-01 -.1209718E-01 -.1098399E-01
-.9148456E-02 -.7211587E-02 -.5598882E-02 -.4332776E-02 -.3016100E-02
-.1208796E-02 .1051286E-02 .3165395E-02 .4405513E-02 .4396087E-02
.3297939E-02 .1655353E-02 .7416014E-04 -.1064366E-02 -.1709471E-02
-.2034871E-02 -.2232056E-02 -.2305923E-02 -.2068891E-02 -.1321939E-02
-.8340139E-04 .1261539E-02 .2134704E-02 .2109285E-02 .1105460E-02
-.5894554E-03 -.2470004E-02 -.4065335E-02 -.5114128E-02 -.5622219E-02
-.5758567E-02 -.5625456E-02 -.5164963E-02 -.4249916E-02 -.2873503E-02
-.1300094E-02 -.4597824E-05 .5635215E-03 .2430489E-03 -.7996342E-03
-.2202475E-02 -.3517325E-02 -.4370145E-02 -.4594031E-02 -.4247630E-02
-.3535756E-02 -.2695440E-02 -.1891009E-02 -.1114788E-02 -.2001966E-03
.1063983E-02 .2729472E-02 .4506580E-02 .5904495E-02 .6538514E-02
.6304454E-02 .5380864E-02 .4057021E-02 .2569189E-02 .1081627E-02
-.2431288E-03 -.1132123E-02 -.1222615E-02 -.2623135E-03 .1692263E-02
.4233489E-02 .6691237E-02 .8351827E-02 .8781418E-02 .7993135E-02
.6368712E-02 .4417529E-02 .2553299E-02 .9862523E-03 -.2879211E-03
-.1353996E-02 -.2215209E-02 -.2753861E-02 -.2802267E-02 -.2290656E-02
-.1316115E-02 -.6447474E-04 .1257941E-02 .2452606E-02 .3298001E-02
.3545191E-02 .3020925E-02 .1768330E-02 .1555108E-03 -.1161917E-02
-.1511165E-02 -.5886191E-03 .1294113E-02 .3326199E-02 .4659119E-02
.4817445E-02 .3850162E-02 .2221826E-02 .5993848E-03 -.3028908E-03
.4499610E-04 .1757564E-02 .4515811E-02 .7747015E-02 .1080406E-01
.1304353E-01 .1395124E-01 .1333330E-01 .1139073E-01 .8626151E-02
.5657044E-02 .2991235E-02 .8765410E-03 -.7137361E-03 -.1944390E-02
-.2910045E-02 -.3511966E-02 -.3524041E-02 -.2828079E-02 -.1517566E-02
.1316982E-03 .1753455E-02 .3100957E-02 .4107929E-02 .4813638E-02
.5258774E-02 .5355058E-02 .4944081E-02 .3965876E-02 .2490912E-02
.6955959E-03 -.1175544E-02 -.2833030E-02 -.3900410E-02 -.3998603E-02
-.2931521E-02 -.8106600E-03 .2012093E-02 .5086689E-02 .7945016E-02
.1019194E-01 .1156584E-01 .1204074E-01 .1182632E-01 .1122714E-01
.1050766E-01 .9726859E-02 .8686699E-02 .7115794E-02 .4980392E-02
.2607636E-02 .4853645E-03 -.1043528E-02 -.2024556E-02 -.2885799E-02
-.4165203E-02 -.6174022E-02 -.8735769E-02 -.1119461E-01 -.1278969E-01
-.1308059E-01 -.1213089E-01 -.1045354E-01 -.8704736E-02 -.7365831E-02
-.6595419E-02 -.6254681E-02 -.6079807E-02 -.5833011E-02 -.5404578E-02
-.4812268E-02 -.4096855E-02 -.3282374E-02 -.2382386E-02 -.1371636E-02
-.2345010E-03 .1029128E-02 .2427386E-02 .3910080E-02 .5297981E-02
.6098532E-02 .5564267E-02 .3285883E-02 -.4251326E-03 -.4773041E-02
-.8874927E-02 -.1201173E-01 -.1370493E-01 -.1378610E-01 -.1244907E-01
-.1014931E-01 -.7440821E-02 -.4775990E-02 -.2391629E-02 -.3412078E-03
.1422210E-02 .2980469E-02 .4361524E-02 .5424185E-02 .5920223E-02
.5656604E-02 .4614099E-02 .2987099E-02 .1059008E-02 -.9265047E-03
-.2832559E-02 -.4642235E-02 -.6381750E-02 -.7981423E-02 -.9160859E-02
-.9536279E-02 -.8840301E-02 -.7065088E-02 -.4407939E-02 -.1073603E-02
.2747630E-02 .6774304E-02 .1060120E-01 .1376236E-01 .1586998E-01
.1671592E-01 .1635498E-01 .1510440E-01 .1335503E-01 .1139606E-01
.9353882E-02 .7269033E-02 .5321099E-02 .3970154E-02 .3756399E-02
.4945238E-02 .7369704E-02 .1047504E-01 .1341277E-01 .1519901E-01
.1512407E-01 .1319094E-01 .1012762E-01 .6957527E-02 .4458216E-02
.2895137E-02 .2114719E-02 .1841946E-02 .1945955E-02 .2461216E-02
.3432909E-02 .4777061E-02 .6226826E-02 .7409148E-02 .8047544E-02
.8084369E-02 .7690493E-02 .7230160E-02 .7136600E-02 .7601987E-02
.8262116E-02 .8252509E-02 .6705430E-02 .3405972E-02 -.9392068E-03
-.5076408E-02 -.7945572E-02 -.9148912E-02 -.8901434E-02 -.7674806E-02
-.5875240E-02 -.3781398E-02 -.1573361E-02 .5790951E-03 .2473429E-02
.3977300E-02 .5076070E-02 .5860220E-02 .6468372E-02 .6916667E-02
.7024350E-02 .6537226E-02 .5305538E-02 .3436577E-02 .1270087E-02
-.7914175E-03 -.2458895E-02 -.3683615E-02 -.4640394E-02 -.5600477E-02
-.6752352E-02 -.8119755E-02 -.9593167E-02 -.1103590E-01 -.1236491E-01
-.1357366E-01 -.1471308E-01 -.1572562E-01 -.1632927E-01 -.1607012E-01
-.1455760E-01 -.1170511E-01 -.7707945E-02 -.2845726E-02 .2593552E-02
.8239099E-02 .1371021E-01 .1861603E-01 .2247447E-01 .2484857E-01
.2550516E-01 .2458508E-01 .2256438E-01 .2001827E-01 .1742999E-01
.1506325E-01 .1292138E-01 .1082082E-01 .8621126E-02 .6408467E-02
.4540162E-02 .3447775E-02 .3325761E-02 .4038313E-02 .5274964E-02
.6656132E-02 .7902636E-02 .8921166E-02 .9655239E-02 .9989046E-02
.9732258E-02 .8780388E-02 .7159844E-02 .4964532E-02 .2307752E-02
-.6598585E-03 -.3801772E-02 -.7083769E-02 -.1046226E-01 -.1390451E-01
-.1739559E-01 -.2086425E-01 -.2418894E-01 -.2721328E-01 -.2965826E-01
-.3105506E-01 -.3092283E-01 -.2907427E-01 -.2567129E-01 -.2091488E-01
-.1488636E-01 -.7665321E-02 .6185650E-03 .9603872E-02 .1826989E-01
.2490295E-01 .2781942E-01 .2632389E-01 .2106028E-01 .1353917E-01
.5409180E-02 -.2194669E-02 -.8995550E-02 -.1531422E-01 -.2164288E-01
-.2810458E-01 -.3396651E-01 -.3805669E-01 -.3954256E-01 -.3823027E-01
-.3463815E-01 -.2965534E-01 -.2389699E-01 -.1754370E-01 -.1032278E-01
-.1798833E-02 .7899254E-02 .1774161E-01 .2585594E-01 .3026364E-01
.3001526E-01 .2568086E-01 .1872162E-01 .1064011E-01 .2562814E-02
-.5082948E-02 -.1227003E-01 -.1900838E-01 -.2517957E-01 -.3042440E-01
-.3433626E-01 -.3669936E-01 -.3742471E-01 -.3640279E-01 -.3380826E-01
-.3004120E-01 -.2557789E-01 -.2102416E-01 -.1677586E-01 -.1282777E-01
-.8711441E-02 -.3827573E-02 .1826430E-02 .7539540E-02 .1232010E-01
.1540526E-01 .1665511E-01 .1650193E-01 .1552809E-01 .1404259E-01
.1197892E-01 .9178393E-02 .5548425E-02 .1189046E-02 -.3380682E-02
-.7406748E-02 -.1015732E-01 -.1119443E-01 -.1066680E-01 -.9078330E-02
-.6958574E-02 -.4656017E-02 -.2360902E-02 -.7646585E-04 .2316568E-02
.4821873E-02 .7298118E-02 .9270378E-02 .9896334E-02 .8407688E-02
.4582978E-02 -.1039746E-02 -.7169492E-02 -.1217499E-01 -.1481022E-01
-.1481108E-01 -.1275542E-01 -.9714298E-02 -.6894851E-02 -.5083874E-02
-.4659003E-02 -.5491210E-02 -.7023740E-02 -.8844939E-02 -.1069379E-01
-.1250066E-01 -.1454846E-01 -.1707686E-01 -.1989536E-01 -.2233137E-01
-.2337480E-01 -.2216943E-01 -.1848253E-01 -.1266237E-01 -.5488932E-02
.1933627E-02 .8465627E-02 .1329814E-01 .1618511E-01 .1730439E-01
.1705052E-01 .1586226E-01 .1402368E-01 .1162920E-01 .8715639E-02
.5517490E-02 .2446470E-02 -.6760063E-04 -.1730739E-02 -.2643284E-02
-.3230591E-02 -.4078908E-02 -.5544881E-02 -.7297788E-02 -.8575398E-02
-.8701870E-02 -.7335231E-02 -.4596454E-02 -.1089148E-02 .2308446E-02
.4930489E-02 .6542544E-02 .7365063E-02 .8007596E-02 .8993838E-02
.1061887E-01 .1289529E-01 .1537708E-01 .1740325E-01 .1855977E-01
.1879475E-01 .1823605E-01 .1728071E-01 .1636020E-01 .1557678E-01
.1487995E-01 .1410674E-01 .1312163E-01 .1187395E-01 .1047366E-01
.9401153E-02 .8976578E-02 .9189091E-02 .9935435E-02 .1091718E-01
.1178125E-01 .1219021E-01 .1208668E-01 .1164181E-01 .1092886E-01
.1033433E-01 .1044840E-01 .1168691E-01 .1398808E-01 .1657557E-01
.1843491E-01 .1882112E-01 .1750543E-01 .1453506E-01 .1014117E-01
.4922797E-02 -.1521883E-03 -.3943517E-02 -.5637022E-02 -.5201442E-02
-.3427316E-02 -.1447996E-02 -.3824361E-03 -.9026158E-03 -.2899126E-02
-.5725932E-02 -.8629017E-02 -.1101399E-01 -.1248361E-01 -.1285285E-01
-.1218286E-01 -.1059188E-01 -.8178625E-02 -.4990100E-02 -.1028538E-02
.3150664E-02 .6362955E-02 .7546259E-02 .6259687E-02 .2864968E-02
-.1635834E-02 -.6229456E-02 -.1030984E-01 -.1363929E-01 -.1633579E-01
-.1859400E-01 -.2033242E-01 -.2129428E-01 -.2115705E-01 -.1995381E-01
-.1803703E-01 -.1572271E-01 -.1346108E-01 -.1157558E-01 -.1028342E-01
-.9973393E-02 -.1088589E-01 -.1283440E-01 -.1521976E-01 -.1700754E-01
-.1700241E-01 -.1464764E-01 -.1020196E-01 -.4479077E-02 .1320025E-02
.6097918E-02 .9342135E-02 .1111878E-01 .1183866E-01 .1189031E-01
.1125836E-01 .9810133E-02 .7402317E-02 .4095284E-02 .1828470E-03
-.4088318E-02 -.8438761E-02 -.1270788E-01 -.1650415E-01 -.1902820E-01
-.1941728E-01 -.1716774E-01 -.1240415E-01 -.5653437E-02 .2476229E-02
.1130678E-01 .1996734E-01 .2754194E-01 .3310088E-01 .3597632E-01
.3620774E-01 .3446190E-01 .3184828E-01 .2958874E-01 .2850179E-01
.2876039E-01 .2975068E-01 .3013135E-01 .2831505E-01 .2328487E-01
.1514390E-01 .4924578E-02 -.5911273E-02 -.1587064E-01 -.2387583E-01
-.2952881E-01 -.3279504E-01 -.3384369E-01 -.3301784E-01 -.3063855E-01
-.2689125E-01 -.2179400E-01 -.1538950E-01 -.7952897E-02 -.9614722E-06
.7845226E-02 .1515901E-01 .2172301E-01 .2721474E-01 .3118629E-01
.3321061E-01 .3315358E-01 .3141515E-01 .2869304E-01 .2566259E-01
.2279068E-01 .2024065E-01 .1800577E-01 .1598535E-01 .1414388E-01
.1245735E-01 .1078543E-01 .8834504E-02 .6219473E-02 .2691722E-02
-.1693293E-02 -.6300586E-02 -.1002109E-01 -.1160434E-01 -.1034660E-01
-.6522354E-02 -.1193026E-02 .4069759E-02 .7675377E-02 .8652136E-02
.7037568E-02 .3689011E-02 -.1703597E-03 -.3524754E-02 -.5959860E-02
-.7514396E-02 -.8400751E-02 -.8698959E-02 -.8162413E-02 -.6605212E-02
-.4054499E-02 -.7536645E-03 .2726845E-02 .5629213E-02 .7297529E-02
.7477676E-02 .6330082E-02 .4341098E-02 .2067891E-02 -.9504297E-04
-.1997928E-02 -.3688179E-02 -.5087495E-02 -.5982067E-02 -.6480055E-02
-.7252330E-02 -.9021151E-02 -.1171408E-01 -.1436533E-01 -.1583704E-01
-.1554842E-01 -.1380533E-01 -.1170943E-01 -.1055543E-01 -.1110769E-01
-.1302097E-01 -.1486034E-01 -.1504907E-01 -.1265275E-01 -.7609021E-02
-.6534508E-03 .7069084E-02 .1429999E-01 .1976389E-01 .2237736E-01
.2172482E-01 .1833358E-01 .1331135E-01 .7787639E-02 .2693778E-02
-.1145081E-02 -.3063542E-02 -.2688044E-02 -.1992260E-03 .3516970E-02
.7112396E-02 .9205986E-02 .8945032E-02 .6403670E-02 .2372275E-02
-.2278300E-02 -.6769582E-02 -.1058472E-01 -.1341400E-01 -.1473648E-01
-.1408597E-01 -.1135366E-01 -.6882144E-02 -.1458501E-02 .4002616E-02
.8496996E-02 .1090352E-01 .1052907E-01 .7505903E-02 .2602705E-02
-.3152815E-02 -.8746525E-02 -.1308470E-01 -.1511365E-01 -.1421543E-01
-.1043071E-01 -.4478917E-02 .2554198E-02 .9666669E-02 .1596795E-01
.2044187E-01 .2229090E-01 .2108773E-01 .1676433E-01 .9953076E-02
.2087643E-02 -.4699501E-02 -.8461761E-02 -.8571464E-02 -.5788908E-02
-.1529597E-02 .2786955E-02 .6275468E-02 .8679429E-02 .1002905E-01
.1042510E-01 .1004069E-01 .9072301E-02 .7679290E-02 .6074455E-02
.4473075E-02 .3067263E-02 .1876768E-02 .8717062E-03 .3874273E-04
-.7671303E-03 -.1613149E-02 -.2440585E-02 -.3194246E-02 -.3793401E-02
-.4178739E-02 -.4371920E-02 -.4428270E-02 -.4444974E-02 -.4507241E-02
-.4583253E-02 -.4686428E-02 -.4835609E-02 -.4934173E-02 -.5009819E-02
-.5108526E-02 -.5215524E-02 -.5363476E-02 -.5621534E-02 -.6092668E-02
-.6926939E-02 -.8356982E-02 -.1046107E-01 -.1306359E-01 -.1585233E-01
-.1816486E-01 -.1922537E-01 -.1863163E-01 -.1631364E-01 -.1262393E-01
-.7983982E-02 -.2435903E-02 .3674736E-02 .9291033E-02 .1301129E-01
.1388301E-01 .1192520E-01 .7868640E-02 .2877415E-02 -.2157623E-02
-.6911937E-02 -.1124143E-01 -.1500486E-01 -.1768727E-01 -.1852320E-01
-.1697383E-01 -.1298640E-01 -.7084300E-02 -.7721108E-04 .7034756E-02
.1297544E-01 .1665017E-01 .1779682E-01 .1692438E-01 .1496840E-01
.1272775E-01 .1023923E-01 .6891558E-02 .1983961E-02 -.4715016E-02
-.1287864E-01 -.2164600E-01 -.2960101E-01 -.3515917E-01 -.3713777E-01
-.3533459E-01 -.3063518E-01 -.2440813E-01 -.1781452E-01 -.1142552E-01
-.5141503E-02 .1498338E-02 .8866538E-02 .1688690E-01 .2470682E-01
.3058300E-01 .3238546E-01 .2889857E-01 .2094647E-01 .1083059E-01
.9575448E-03 -.7089134E-02 -.1283213E-01 -.1619175E-01 -.1684417E-01
-.1445279E-01 -.9549346E-02 -.3879482E-02 .4078140E-03 .1928972E-02
.7526545E-03 -.1856707E-02 -.4191256E-02 -.4972957E-02 -.3965310E-02
-.2070338E-02 -.7092010E-03 -.9489900E-03 -.3100835E-02 -.6707078E-02
-.1080124E-01 -.1430832E-01 -.1612181E-01 -.1518834E-01 -.1123060E-01
-.4823611E-02 .3024228E-02 .1071457E-01 .1645373E-01 .1889990E-01
.1757643E-01 .1320395E-01 .7106346E-02 .5534812E-03 -.5513032E-02
-.1061932E-01 -.1439279E-01 -.1645384E-01 -.1672504E-01 -.1540873E-01
-.1275695E-01 -.9118096E-02 -.4867156E-02 -.2455867E-03 .4230080E-02
.7672429E-02 .9142766E-02 .8019062E-02 .4453303E-02 -.8225852E-03
-.6841658E-02 -.1275177E-01 -.1813499E-01 -.2261233E-01 -.2564713E-01
-.2684913E-01 -.2616440E-01 -.2380227E-01 -.1977725E-01 -.1365101E-01
-.5088131E-02 .5197379E-02 .1550537E-01 .2408050E-01 .3004262E-01
.3360170E-01 .3521722E-01 .3522096E-01 .3380402E-01 .3094955E-01
.2685286E-01 .2192577E-01 .1657763E-01 .1121935E-01 .6148842E-02
.1575380E-02 -.2472731E-02 -.6185611E-02 -.9684531E-02 -.1308251E-01
-.1661101E-01 -.2043943E-01 -.2465044E-01 -.2891475E-01 -.3214394E-01
-.3306835E-01 -.3093236E-01 -.2593347E-01 -.1928827E-01 -.1274861E-01
-.7825682E-02 -.5133567E-02 -.4425851E-02 -.4818741E-02 -.5252232E-02
-.4930240E-02 -.3672806E-02 -.2020577E-02 -.8254652E-03 -.8623670E-03
-.2420186E-02 -.5125718E-02 -.8013458E-02 -.9941214E-02 -.1001373E-01
-.7816071E-02 -.3770037E-02 .1162191E-02 .6006638E-02 .9756063E-02
.1172414E-01 .1180266E-01 .1034587E-01 .8106998E-02 .5764324E-02
.3702295E-02 .2109327E-02 .8522273E-03 -.4087143E-03 -.1958545E-02
-.3776898E-02 -.5280190E-02 -.5708328E-02 -.4667265E-02 -.2201315E-02
.1272191E-02 .5028644E-02 .8190299E-02 .1023330E-01 .1121147E-01
.1140253E-01 .1117003E-01 .1078246E-01 .1021337E-01 .9389039E-02
.8031923E-02 .5774986E-02 .2565933E-02 -.1537241E-02 -.6345046E-02
-.1170225E-01 -.1765976E-01 -.2419191E-01 -.3101631E-01 -.3736947E-01
-.4232024E-01 -.4541069E-01 -.4652028E-01 -.4578869E-01 -.4361636E-01
-.4038329E-01 -.3636168E-01 -.3167082E-01 -.2655599E-01 -.2157473E-01
-.1738166E-01 -.1447114E-01 -.1283825E-01 -.1207965E-01 -.1171937E-01
-.1129131E-01 -.1059250E-01 -.9596382E-02 -.8229236E-02 -.6418498E-02
-.4035644E-02 -.1143476E-02 .2077366E-02 .5351418E-02 .8341142E-02
.1100083E-01 .1331432E-01 .1519221E-01 .1643695E-01 .1674342E-01
.1592628E-01 .1411161E-01 .1171599E-01 .9296672E-02 .7472793E-02
.6577241E-02 .6637887E-02 .7456219E-02 .8644244E-02 .1005389E-01
.1186538E-01 .1417436E-01 .1673345E-01 .1899539E-01 .2042756E-01
.2091466E-01 .2054275E-01 .1939914E-01 .1767041E-01 .1535188E-01
.1237763E-01 .8704570E-02 .4147236E-02 -.1360430E-02 -.7787316E-02
-.1486702E-01 -.2177232E-01 -.2736238E-01 -.3066875E-01 -.3144420E-01
-.3027946E-01 -.2814377E-01 -.2601297E-01 -.2455224E-01 -.2386868E-01
-.2363605E-01 -.2353825E-01 -.2333943E-01 -.2288862E-01 -.2227907E-01
-.2161968E-01 -.2099637E-01 -.2040827E-01 -.1973868E-01 -.1893775E-01
-.1797027E-01 -.1667843E-01 -.1487935E-01 -.1246686E-01 -.9483893E-02
-.6337230E-02 -.3506977E-02 -.1291572E-02 .2429348E-03 .1248035E-02
.1976424E-02 .2601214E-02 .2968768E-02 .2795946E-02 .1798981E-02
-.1659973E-03 -.2845534E-02 -.5849434E-02 -.8908303E-02 -.1182109E-01
-.1452760E-01 -.1696396E-01 -.1883610E-01 -.1968982E-01 -.1901421E-01
-.1669585E-01 -.1313140E-01 -.8861982E-02 -.4456569E-02 -.3243527E-03
.3309549E-02 .6103826E-02 .7759009E-02 .8226937E-02 .7582430E-02
.6252748E-02 .4737565E-02 .3470717E-02 .3108701E-02 .4169565E-02
.6781691E-02 .1079032E-01 .1556154E-01 .2013236E-01 .2382233E-01
.2626433E-01 .2745505E-01 .2788424E-01 .2811386E-01 .2843440E-01
.2895634E-01 .2960218E-01 .3003924E-01 .3003745E-01 .2953782E-01
.2847969E-01 .2723935E-01 .2630562E-01 .2600641E-01 .2686385E-01
.2900223E-01 .3201463E-01 .3563619E-01 .3949034E-01 .4313774E-01
.4661442E-01 .4993431E-01 .5291145E-01 .5533222E-01 .5665800E-01
.5645912E-01 .5503413E-01 .5279327E-01 .5007040E-01 .4737852E-01
.4487943E-01 .4235794E-01 .3974572E-01 .3686028E-01 .3368732E-01
.3055061E-01 .2781146E-01 .2609253E-01 .2599445E-01 .2757407E-01
.3018356E-01 .3314234E-01 .3576748E-01 .3741581E-01 .3763105E-01
.3611741E-01 .3313879E-01 .2902728E-01 .2419680E-01 .1935050E-01
.1467773E-01 .1004832E-01 .5684898E-02 .1508315E-02 -.2792435E-02
-.7023769E-02 -.1107366E-01 -.1449093E-01 -.1658374E-01 -.1736586E-01
-.1709493E-01 -.1650190E-01 -.1654562E-01 -.1754671E-01 -.1935994E-01
-.2175887E-01 -.2421596E-01 -.2633406E-01 -.2826962E-01 -.3007234E-01
-.3178528E-01 -.3368383E-01 -.3588525E-01 -.3838538E-01 -.4116496E-01
-.4399211E-01 -.4660682E-01 -.4828366E-01 -.4786318E-01 -.4458027E-01
-.3845997E-01 -.3070465E-01 -.2270198E-01 -.1560400E-01 -.1010790E-01
-.5980578E-02 -.2772934E-02 -.1713775E-04 .2673980E-02 .5519567E-02
.8460680E-02 .1130877E-01 .1401008E-01 .1656949E-01 .1883679E-01
.2051101E-01 .2146666E-01 .2153975E-01 .2084240E-01 .1990095E-01
.1882448E-01 .1767300E-01 .1666248E-01 .1545950E-01 .1377915E-01
.1181045E-01 .9534539E-02 .7009097E-02 .4686050E-02 .2836866E-02
.1815343E-02 .1898834E-02 .2840229E-02 .4322539E-02 .5996240E-02
.7348233E-02 .7878039E-02 .7258954E-02 .5524328E-02 .2910175E-02
-.1192840E-03 -.2709780E-02 -.4433065E-02 -.5095499E-02 -.4517425E-02
-.3210972E-02 -.1762970E-02 -.3308239E-03 .6434073E-03 .5748676E-03
-.3093338E-03 -.1867950E-02 -.4005723E-02 -.6188342E-02 -.8441897E-02
-.1051722E-01 -.1218090E-01 -.1371676E-01 -.1513981E-01 -.1640948E-01
-.1758842E-01 -.1881650E-01 -.2002446E-01 -.2117680E-01 -.2218898E-01
-.2237774E-01 -.2098826E-01 -.1749785E-01 -.1199713E-01 -.5457907E-02
.1176037E-02 .6830883E-02 .1082376E-01 .1318703E-01 .1378624E-01
.1277508E-01 .1066039E-01 .7909360E-02 .4877214E-02 .1911501E-02
-.9173029E-03 -.3531967E-02 -.5644893E-02 -.7227729E-02 -.8260763E-02
-.8706863E-02 -.8740882E-02 -.8710160E-02 -.8780833E-02 -.8929077E-02
-.9381925E-02 -.1024996E-01 -.1161204E-01 -.1344303E-01 -.1545313E-01
-.1776305E-01 -.2021068E-01 -.2251479E-01 -.2493718E-01 -.2747284E-01
-.2994092E-01 -.3222728E-01 -.3362418E-01 -.3316205E-01 -.2992824E-01
-.2359221E-01 -.1504628E-01 -.5412169E-02 .4361812E-02 .1344546E-01
.2143670E-01 .2826677E-01 .3386766E-01 .3807828E-01 .4080334E-01
.4204068E-01 .4194099E-01 .4092351E-01 .3947267E-01 .3785792E-01
.3639122E-01 .3513104E-01 .3384689E-01 .3237553E-01 .3047683E-01
.2801395E-01 .2530326E-01 .2283371E-01 .2080132E-01 .1963958E-01
.1924832E-01 .1914892E-01 .1924157E-01 .1914600E-01 .1868474E-01
.1764816E-01 .1598250E-01 .1400698E-01 .1185010E-01 .9535737E-02
.7303359E-02 .5424927E-02 .4099336E-02 .3602893E-02 .3813768E-02
.4691968E-02 .5982851E-02 .7198455E-02 .8475807E-02 .9704463E-02
.1079903E-01 .1199522E-01 .1309921E-01 .1375677E-01 .1337324E-01
.1161517E-01 .8665868E-02 .4754639E-02 .3327197E-03 -.4175680E-02
-.8638803E-02 -.1292470E-01 -.1677962E-01 -.2002213E-01 -.2245581E-01
-.2394014E-01 -.2436307E-01 -.2341324E-01 -.2102749E-01 -.1757305E-01
-.1357719E-01 -.9647067E-02 -.6237683E-02 -.3523645E-02 -.1619959E-02
-.5938797E-03 -.5873712E-03 -.1729091E-02 -.3658303E-02 -.6054787E-02
-.8687612E-02 -.1110900E-01 -.1329320E-01 -.1517378E-01 -.1627424E-01
-.1622413E-01 -.1470082E-01 -.1182905E-01 -.8394616E-02 -.5225185E-02
-.2862584E-02 -.1459262E-02 -.7402939E-03 -.3132338E-03 .1528705E-03
.9427832E-03 .2164652E-02 .3504850E-02 .4956621E-02 .6561044E-02
.7926335E-02 .9208951E-02 .1042562E-01 .1143696E-01 .1241714E-01
.1325782E-01 .1376058E-01 .1387821E-01 .1354292E-01 .1270655E-01
.1164777E-01 .1048626E-01 .8795471E-02 .5968882E-02 .1222028E-02
-.5439779E-02 -.1263862E-01 -.1905841E-01 -.2380396E-01 -.2617362E-01
-.2597806E-01 -.2335960E-01 -.1890640E-01 -.1330358E-01 -.7446688E-02
-.2739187E-02 .2071204E-03 .1429036E-02 .1028736E-02 -.2401374E-03
-.1668075E-02 -.2988337E-02 -.4067810E-02 -.5173909E-02 -.6483773E-02
-.7750192E-02 -.8730304E-02 -.9183981E-02 -.8757737E-02 -.7443620E-02
-.5621340E-02 -.3618324E-02 -.1583880E-02 .3090976E-03 .1956134E-02
.3648367E-02 .5579855E-02 .7890413E-02 .1081725E-01 .1419929E-01
.1720072E-01 .1915609E-01 .1985558E-01 .1925274E-01 .1788514E-01
.1630675E-01 .1477740E-01 .1338977E-01 .1196436E-01 .1022575E-01
.8064738E-02 .5583655E-02 .3000570E-02 .5331712E-03 -.1379636E-02
-.2426841E-02 -.2616121E-02 -.2013066E-02 -.1077223E-02 -.2170750E-03
.2452922E-03 -.1324171E-03 -.1388844E-02 -.3343876E-02 -.5693895E-02
-.8076184E-02 -.1041739E-01 -.1256042E-01 -.1441242E-01 -.1609455E-01
-.1746467E-01 -.1859309E-01 -.1958296E-01 -.2029965E-01 -.2088517E-01
-.2118079E-01 -.2093396E-01 -.2022643E-01 -.1911941E-01 -.1776040E-01
-.1627488E-01 -.1464481E-01 -.1261195E-01 -.9632813E-02 -.5389511E-02
-.7220652E-04 .5642729E-02 .1097859E-01 .1545286E-01 .1863852E-01
.2052978E-01 .2124407E-01 .2077740E-01 .1950230E-01 .1778722E-01
.1596091E-01 .1415971E-01 .1250467E-01 .1113676E-01 .1000036E-01
.9128085E-02 .8438501E-02 .7807495E-02 .7209282E-02 .6562780E-02
.5611157E-02 .4104884E-02 .1836216E-02 -.1142869E-02 -.4689413E-02
-.8478513E-02 -.1198585E-01 -.1467946E-01 -.1589029E-01 -.1564526E-01
-.1418316E-01 -.1176374E-01 -.8850510E-02 -.5673143E-02 -.2510591E-02
.1802678E-03 .2025823E-02 .3032400E-02 .3425292E-02 .3508645E-02
.3676629E-02 .4091425E-02 .4682304E-02 .5472754E-02 .6452162E-02
.7347295E-02 .7999419E-02 .8296611E-02 .7928995E-02 .6813268E-02
.5011843E-02 .2802597E-02 .4992408E-03 -.1831118E-02 -.4069736E-02
-.6046223E-02 -.7749683E-02 -.9061467E-02 -.9705197E-02 -.9636080E-02
-.9016790E-02 -.8174797E-02 -.7198690E-02 -.6204959E-02 -.5373029E-02
-.4344859E-02 -.2803768E-02 -.5525524E-03 .2538277E-02 .6235031E-02
.1016038E-01 .1387329E-01 .1677467E-01 .1827768E-01 .1816112E-01
.1658768E-01 .1412021E-01 .1139746E-01 .8696474E-02 .6208537E-02
.3969926E-02 .1747097E-02 -.5591269E-03 -.2936676E-02 -.5368648E-02
-.7513072E-02 -.8918480E-02 -.9102443E-02 -.7764897E-02 -.5171679E-02
-.1761796E-02 .1879600E-02 .5135838E-02 .7591760E-02 .9053612E-02
.9685827E-02 .9766829E-02 .9580221E-02 .9451962E-02 .9444144E-02
.9532332E-02 .9651165E-02 .9750393E-02 .9941313E-02 .1041583E-01
.1151542E-01 .1339452E-01 .1591906E-01 .1877037E-01 .2152736E-01
.2384239E-01 .2559043E-01 .2692286E-01 .2806866E-01 .2907231E-01
.2980794E-01 .3000675E-01 .2939602E-01 .2797701E-01 .2590633E-01
.2358968E-01 .2134617E-01 .1922584E-01 .1733120E-01 .1554119E-01
.1381606E-01 .1229399E-01 .1111732E-01 .1066441E-01 .1103057E-01
.1204131E-01 .1338293E-01 .1450729E-01 .1504738E-01 .1478665E-01
.1374486E-01 .1226510E-01 .1067453E-01 .9171079E-02 .7956521E-02
.7243112E-02 .7133591E-02 .7548334E-02 .8351206E-02 .9329349E-02
.1033786E-01 .1134440E-01 .1219463E-01 .1297151E-01 .1378645E-01
.1454178E-01 .1516388E-01 .1552029E-01 .1536431E-01 .1441286E-01
.1276976E-01 .1072529E-01 .8463387E-02 .6392266E-02 .4752372E-02
.3457876E-02 .2437014E-02 .1596681E-02 .5628094E-03 -.1049293E-02
-.3360829E-02 -.6548210E-02 -.1039057E-01 -.1454904E-01 -.1884158E-01
-.2302796E-01 -.2708942E-01 -.3096956E-01 -.3458155E-01 -.3761068E-01
-.3953193E-01 -.4003705E-01 -.3918434E-01 -.3738913E-01 -.3518965E-01
-.3320436E-01 -.3165300E-01 -.3067854E-01 -.3044658E-01 -.3076029E-01
-.3149571E-01 -.3248234E-01 -.3358702E-01 -.3468694E-01 -.3569531E-01
-.3663265E-01 -.3748605E-01 -.3817463E-01 -.3864487E-01 -.3896101E-01
-.3913047E-01 -.3926949E-01 -.3946818E-01 -.3963120E-01 -.3984169E-01
-.4010381E-01 -.4030988E-01 -.4048733E-01 -.4068742E-01 -.4091181E-01
-.4108314E-01 -.4134737E-01 -.4164515E-01 -.4191625E-01 -.4221260E-01
-.4250566E-01 -.4284395E-01 -.4312954E-01 -.4330667E-01 -.4334183E-01
-.4308501E-01 -.4222350E-01 -.4073786E-01 -.3880898E-01 -.3658534E-01
-.3430503E-01 -.3193513E-01 -.2939320E-01 -.2643007E-01 -.2268951E-01
-.1838666E-01 -.1380213E-01 -.9347827E-02 -.5776366E-02 -.3465915E-02
-.2368894E-02 -.2129215E-02 -.2258109E-02 -.2351747E-02 -.2196853E-02
-.1854580E-02 -.1449342E-02 -.9127997E-03 -.1951298E-03 .7112905E-03
.2021865E-02 .3688877E-02 .5545756E-02 .7607521E-02 .9759494E-02
.1173481E-01 .1307648E-01 .1348676E-01 .1280592E-01 .1113297E-01
.8912421E-02 .6391149E-02 .3905114E-02 .1601427E-02 -.6692400E-03
-.2717528E-02 -.4174201E-02 -.4627424E-02 -.3727393E-02 -.1547067E-02
.1500089E-02 .4950034E-02 .8360430E-02 .1141537E-01 .1397295E-01
.1600148E-01 .1754447E-01 .1868035E-01 .1966211E-01 .2055389E-01
.2138742E-01 .2237416E-01 .2335756E-01 .2431197E-01 .2537448E-01
.2656551E-01 .2791929E-01 .2965010E-01 .3174222E-01 .3383392E-01
.3578273E-01 .3710474E-01 .3739960E-01 .3679326E-01 .3536805E-01
.3346081E-01 .3159161E-01 .2981960E-01 .2811941E-01 .2658930E-01
.2499555E-01 .2309543E-01 .2088378E-01 .1841231E-01 .1567525E-01
.1284477E-01 .1015462E-01 .7788366E-02 .6110006E-02 .5402534E-02
.5638440E-02 .6563084E-02 .7850387E-02 .9206303E-02 .1031194E-01
.1098126E-01 .1114542E-01 .1074463E-01 .9957830E-02 .8947896E-02
.7833102E-02 .6707737E-02 .5635129E-02 .4515381E-02 .3035268E-02
.9134036E-03 -.1856914E-02 -.4957733E-02 -.8185762E-02 -.1104210E-01
-.1317374E-01 -.1469623E-01 -.1536370E-01 -.1512908E-01 -.1424074E-01
-.1287377E-01 -.1122237E-01 -.9534758E-02 -.7934308E-02 -.6283240E-02
-.4473937E-02 -.2516351E-02 -.4075612E-03 .1761349E-02 .3631932E-02
.5185014E-02 .6550926E-02 .7627133E-02 .8434078E-02 .8885753E-02
.8896488E-02 .8412189E-02 .7505225E-02 .6435478E-02 .5327768E-02
.4238728E-02 .3224385E-02 .2239655E-02 .1074355E-02 -.3118892E-03
-.1826858E-02 -.3254363E-02 -.4210919E-02 -.4456596E-02 -.3901531E-02
-.2794523E-02 -.1524628E-02 -.2957599E-03 .6724922E-03 .1142362E-02
.1047438E-02 .4277500E-03 -.6598095E-03 -.1989157E-02 -.3279215E-02
-.4515194E-02 -.5755661E-02 -.6996466E-02 -.8379105E-02 -.1009309E-01
-.1231685E-01 -.1502015E-01 -.1803725E-01 -.2123054E-01 -.2439197E-01
-.2726311E-01 -.2962468E-01 -.3126173E-01 -.3180346E-01 -.3114895E-01
-.2959152E-01 -.2749439E-01 -.2527780E-01 -.2314585E-01 -.2111495E-01
-.1910038E-01 -.1695931E-01 -.1471179E-01 -.1232559E-01 -.9847208E-02
-.7431594E-02 -.5289531E-02 -.3606317E-02 -.2274182E-02 -.1241455E-02
-.4807131E-03 .2867630E-03 .1090648E-02 .1916820E-02 .2861809E-02
.3882653E-02 .5057116E-02 .6342610E-02 .7716291E-02 .9006579E-02
.9757320E-02 .9773900E-02 .9016858E-02 .7676922E-02 .5968230E-02
.4131218E-02 .2508963E-02 .1022927E-02 -.4677441E-03 -.1886409E-02
-.3108552E-02 -.4005146E-02 -.4412478E-02 -.4230130E-02 -.3596251E-02
-.2757693E-02 -.1864050E-02 -.9833094E-03 -.2448452E-03 .4160564E-03
.1151147E-02 .1935780E-02 .2790786E-02 .3850240E-02 .5098510E-02
.6396557E-02 .7825588E-02 .9341879E-02 .1066413E-01 .1168302E-01
.1234519E-01 .1258490E-01 .1257384E-01 .1249794E-01 .1239264E-01
.1241063E-01 .1266978E-01 .1319696E-01 .1395792E-01 .1486491E-01
.1588669E-01 .1690460E-01 .1783274E-01 .1878446E-01 .1975388E-01
.2068742E-01 .2156174E-01 .2217118E-01 .2235588E-01 .2188751E-01
.2072463E-01 .1910627E-01 .1721580E-01 .1530059E-01 .1345308E-01
.1165164E-01 .9748835E-02 .7580274E-02 .5059838E-02 .2181048E-02
-.8965093E-03 -.4099195E-02 -.7276131E-02 -.1024968E-01 -.1300149E-01
-.1535646E-01 -.1714553E-01 -.1836106E-01 -.1893160E-01 -.1906335E-01
-.1897772E-01 -.1879010E-01 -.1873646E-01 -.1877756E-01 -.1881406E-01
-.1889539E-01 -.1903846E-01 -.1927963E-01 -.1956152E-01 -.1993420E-01
-.2044804E-01 -.2097104E-01 -.2152963E-01 -.2210045E-01 -.2261029E-01
-.2293475E-01 -.2269995E-01 -.2165200E-01 -.1990207E-01 -.1767281E-01
-.1514958E-01 -.1254377E-01 -.9940813E-02 -.7251846E-02 -.4495867E-02
-.1834558E-02 .3952551E-03 .1854653E-02 .2588015E-02 .2849055E-02
.2815094E-02 .2756684E-02 .2919244E-02 .3275645E-02 .3764375E-02
.4392082E-02 .5123506E-02 .5873642E-02 .6631104E-02 .7435058E-02
.8256780E-02 .9132857E-02 .1018645E-01 .1134536E-01 .1254313E-01
.1377035E-01 .1493849E-01 .1591571E-01 .1646537E-01 .1650017E-01
.1607343E-01 .1528927E-01 .1435211E-01 .1338305E-01 .1244231E-01
.1162597E-01 .1083991E-01 .9958939E-02 .9008164E-02 .7897419E-02
.6446123E-02 .4474120E-02 .2036402E-02 -.5182785E-03 -.2859306E-02
-.4600725E-02 -.5509272E-02 -.5738853E-02 -.5606866E-02 -.5408165E-02
-.5262522E-02 -.5301959E-02 -.5500603E-02 -.5615600E-02 -.5664186E-02
-.5684226E-02 -.5563378E-02 -.5416233E-02 -.5361794E-02 -.5286615E-02
-.5222926E-02 -.5182292E-02 -.5049308E-02 -.4734245E-02 -.4146149E-02
-.3186759E-02 -.1834258E-02 -.3142676E-03 .1142894E-02 .2213497E-02
.2677973E-02 .2576888E-02 .2049605E-02 .1353403E-02 .6370208E-03
.7696284E-04 -.3840222E-03 -.8431719E-03 -.1240350E-02 -.1731362E-02
-.2287197E-02 -.2781299E-02 -.3341096E-02 -.3964879E-02 -.4680874E-02
-.5636460E-02 -.6760819E-02 -.7853342E-02 -.8590249E-02 -.8662570E-02
-.8005848E-02 -.6810741E-02 -.5326752E-02 -.3841953E-02 -.2599307E-02
-.1789202E-02 -.1618141E-02 -.2135000E-02 -.3277990E-02 -.4839744E-02
-.6556841E-02 -.8289200E-02 -.9959017E-02 -.1153790E-01 -.1306828E-01
-.1444045E-01 -.1538276E-01 -.1571496E-01 -.1526215E-01 -.1412375E-01
-.1262103E-01 -.1094714E-01 -.9336365E-02 -.7883712E-02 -.6546693E-02
-.5324920E-02 -.4283892E-02 -.3511433E-02 -.3010718E-02 -.2758518E-02
-.2689395E-02 -.2642917E-02 -.2503958E-02 -.2262002E-02 -.1930234E-02
-.1526137E-02 -.1090235E-02 -.7078323E-03 -.2727131E-03 .3235799E-03
.1020995E-02 .1760420E-02 .2252078E-02 .2182029E-02 .1537890E-02
.3984516E-03 -.1015696E-02 -.2421581E-02 -.3720552E-02 -.4914779E-02
-.6040551E-02 -.7089596E-02 -.8104178E-02 -.9080414E-02 -.9967503E-02
-.1081311E-01 -.1162745E-01 -.1241252E-01 -.1318338E-01 -.1396752E-01
-.1476899E-01 -.1556185E-01 -.1630489E-01 -.1681531E-01 -.1693400E-01
-.1658796E-01 -.1570630E-01 -.1448131E-01 -.1311323E-01 -.1171250E-01
-.1040772E-01 -.9136133E-02 -.7879401E-02 -.6630753E-02 -.5363429E-02
-.4198113E-02 -.3202102E-02 -.2458119E-02 -.1994086E-02 -.1713913E-02
-.1527896E-02 -.1312832E-02 -.1087912E-02 -.8321918E-03 -.5112643E-03
-.2468145E-03 -.8340905E-05 .2647129E-03 .5213375E-03 .7621085E-03
.1038711E-02 .1405533E-02 .1871109E-02 .2460456E-02 .3220863E-02
.4082577E-02 .4985384E-02 .5953673E-02 .6965389E-02 .8070290E-02
.9368096E-02 .1078513E-01 .1215192E-01 .1331484E-01 .1415191E-01
.1469661E-01 .1507143E-01 .1535172E-01 .1564179E-01 .1603185E-01
.1647825E-01 .1692019E-01 .1734562E-01 .1772421E-01 .1808961E-01
.1845048E-01 .1882569E-01 .1927590E-01 .1973504E-01 .2018737E-01
.2068158E-01 .2115884E-01 .2163320E-01 .2215990E-01 .2268229E-01
.2321502E-01 .2379547E-01 .2440197E-01 .2507779E-01 .2581341E-01
.2655814E-01 .2736368E-01 .2822543E-01 .2905153E-01 .2983108E-01
.3059166E-01 .3114084E-01 .3131671E-01 .3111218E-01 .3051305E-01
.2968909E-01 .2874523E-01 .2782100E-01 .2703444E-01 .2629805E-01
.2572662E-01 .2539096E-01 .2512752E-01 .2493473E-01 .2488788E-01
.2479736E-01 .2464927E-01 .2455270E-01 .2447592E-01 .2439927E-01
.2430172E-01 .2426220E-01 .2426375E-01 .2427464E-01 .2437629E-01
.2463915E-01 .2505681E-01 .2559246E-01 .2620214E-01 .2672332E-01
.2697367E-01 .2674980E-01 .2590527E-01 .2449912E-01 .2270682E-01
.2072397E-01 .1872458E-01 .1681497E-01 .1489248E-01 .1273290E-01
.1014357E-01 .6973620E-02 .3233493E-02 -.9093364E-03 -.5091927E-02
-.8996452E-02 -.1249101E-01 -.1546102E-01 -.1801931E-01 -.2037502E-01
-.2256763E-01 -.2452619E-01 -.2615070E-01 -.2741391E-01 -.2833618E-01
-.2901275E-01 -.2964300E-01 -.3027277E-01 -.3094581E-01 -.3169998E-01
-.3237588E-01 -.3285102E-01 -.3302768E-01 -.3291384E-01 -.3256415E-01
-.3206932E-01 -.3161216E-01 -.3117883E-01 -.3075836E-01 -.3038615E-01
-.2997476E-01 -.2949072E-01 -.2889738E-01 -.2808077E-01 -.2703998E-01
-.2581368E-01 -.2444338E-01 -.2305014E-01 -.2163020E-01 -.2024749E-01
-.1895732E-01 -.1765319E-01 -.1636023E-01 -.1508094E-01 -.1385074E-01
-.1283443E-01 -.1209795E-01 -.1174222E-01 -.1181543E-01 -.1213725E-01
-.1254287E-01 -.1296419E-01 -.1333252E-01 -.1364070E-01 -.1391640E-01
-.1419105E-01 -.1445969E-01 -.1465255E-01 -.1477652E-01 -.1483936E-01
-.1483403E-01 -.1476891E-01 -.1473926E-01 -.1479601E-01 -.1485080E-01
-.1496509E-01 -.1515630E-01 -.1534250E-01 -.1553761E-01 -.1575086E-01
-.1592756E-01 -.1601255E-01 -.1584265E-01 -.1520028E-01 -.1407137E-01
-.1244699E-01 -.1044820E-01 -.8386543E-02 -.6500234E-02 -.5004164E-02
-.4060089E-02 -.3704259E-02 -.3819263E-02 -.4212448E-02 -.4642108E-02
-.5014184E-02 -.5350055E-02 -.5604700E-02 -.5835293E-02 -.6090989E-02
-.6358608E-02 -.6583542E-02 -.6632757E-02 -.6375431E-02 -.5748163E-02
-.4755098E-02 -.3513255E-02 -.2183013E-02 -.8565018E-03 .3689066E-03
.1507668E-02 .2576427E-02 .3463083E-02 .4132497E-02 .4564437E-02
.4712152E-02 .4682966E-02 .4604270E-02 .4516317E-02 .4480989E-02
.4495280E-02 .4500414E-02 .4520279E-02 .4620587E-02 .4786315E-02
.5036992E-02 .5389506E-02 .5790761E-02 .6240889E-02 .6682114E-02
.7070261E-02 .7449444E-02 .7773744E-02 .7943765E-02 .7919061E-02
.7642944E-02 .7139513E-02 .6501349E-02 .5792756E-02 .5122678E-02
.4503649E-02 .3894988E-02 .3361803E-02 .2997071E-02 .2824104E-02
.2904937E-02 .3302560E-02 .3891336E-02 .4538610E-02 .5184030E-02
.5764024E-02 .6297548E-02 .6799505E-02 .7280018E-02 .7721628E-02
.8008869E-02 .8101727E-02 .7967058E-02 .7636067E-02 .7217181E-02
.6756832E-02 .6330422E-02 .5956595E-02 .5598731E-02 .5237896E-02
.4858647E-02 .4459606E-02 .4042886E-02 .3623913E-02 .3331852E-02
.3272202E-02 .3539817E-02 .4259329E-02 .5275553E-02 .6439381E-02
.7688936E-02 .8888779E-02 .1003329E-01 .1115511E-01 .1226569E-01
.1334822E-01 .1433992E-01 .1512568E-01 .1551267E-01 .1551125E-01
.1519386E-01 .1465298E-01 .1409188E-01 .1356510E-01 .1306618E-01
.1262291E-01 .1220510E-01 .1181274E-01 .1171580E-01 .1216633E-01
.1331303E-01 .1524950E-01 .1777597E-01 .2057265E-01 .2329719E-01
.2556155E-01 .2701758E-01 .2752620E-01 .2721820E-01 .2634695E-01
.2523897E-01 .2414605E-01 .2319065E-01 .2235554E-01 .2145555E-01
.2050957E-01 .1959173E-01 .1862220E-01 .1762740E-01 .1667050E-01
.1579438E-01 .1496501E-01 .1416144E-01 .1341079E-01 .1265789E-01
.1192034E-01 .1123068E-01 .1052241E-01 .9780018E-02 .9066127E-02
.8375003E-02 .7732417E-02 .7280988E-02 .7107634E-02 .7182783E-02
.7523201E-02 .8096221E-02 .8686772E-02 .9260346E-02 .9837201E-02
.1037292E-01 .1092720E-01 .1152604E-01 .1217660E-01 .1289584E-01
.1365187E-01 .1441366E-01 .1516193E-01 .1588677E-01 .1661195E-01
.1731763E-01 .1802403E-01 .1874635E-01 .1947141E-01 .2022626E-01
.2098042E-01 .2171716E-01 .2234676E-01 .2272494E-01 .2278279E-01
.2250204E-01 .2195459E-01 .2123772E-01 .2048330E-01 .1978113E-01
.1911287E-01 .1846887E-01 .1780861E-01 .1710777E-01 .1637331E-01
.1562000E-01 .1489020E-01 .1419427E-01 .1352737E-01 .1287828E-01
.1223727E-01 .1160183E-01 .1098388E-01 .1035602E-01 .9736413E-02
.9122191E-02 .8497272E-02 .7891950E-02 .7262543E-02 .6617015E-02
.5915304E-02 .5094611E-02 .4203631E-02 .3212819E-02 .2149947E-02
.1075717E-02 -.2298022E-04 -.1061169E-02 -.2113488E-02 -.3225617E-02
-.4313436E-02 -.5379884E-02 -.6293834E-02 -.6921222E-02 -.7212701E-02
-.7168815E-02 -.6906375E-02 -.6550293E-02 -.6189936E-02 -.5934755E-02
-.5930713E-02 -.6242107E-02 -.6902787E-02 -.7896883E-02 -.9142266E-02
-.1054151E-01 -.1194656E-01 -.1327986E-01 -.1458012E-01 -.1583052E-01
-.1691642E-01 -.1787921E-01 -.1868030E-01 -.1925149E-01 -.1966988E-01
-.1994674E-01 -.2009811E-01 -.2020098E-01 -.2034127E-01 -.2048578E-01
-.2063758E-01 -.2081370E-01 -.2104213E-01 -.2142617E-01 -.2201315E-01
-.2286919E-01 -.2394956E-01 -.2515655E-01 -.2643643E-01 -.2769093E-01
-.2888396E-01 -.3001620E-01 -.3110532E-01 -.3219363E-01 -.3319893E-01
-.3405916E-01 -.3485087E-01 -.3563057E-01 -.3637475E-01 -.3708807E-01
-.3781649E-01 -.3847712E-01 -.3887727E-01 -.3890360E-01 -.3847836E-01
-.3756977E-01 -.3635029E-01 -.3502873E-01 -.3377670E-01 -.3253980E-01
-.3136170E-01 -.3024498E-01 -.2883484E-01 -.2709108E-01 -.2495759E-01
-.2241910E-01 -.1970108E-01 -.1696696E-01 -.1436926E-01 -.1195755E-01
-.9720910E-02 -.7672811E-02 -.5682905E-02 -.3770845E-02 -.2103646E-02
-.7040582E-03 .3173436E-03 .9817477E-03 .1355928E-02 .1659764E-02
.2080315E-02 .2497159E-02 .3016884E-02 .3572530E-02 .4154701E-02
.4847182E-02 .5578421E-02 .6394113E-02 .7215470E-02 .8141736E-02
.9062471E-02 .9886537E-02 .1083171E-01 .1185518E-01 .1291593E-01
.1388871E-01 .1453616E-01 .1471435E-01 .1416915E-01 .1297304E-01
.1145868E-01 .9819351E-02 .8270624E-02 .6834504E-02 .5599903E-02
.4676519E-02 .3879176E-02 .3234140E-02 .2802702E-02 .2398671E-02
.1986406E-02 .1559084E-02 .1090037E-02 .5775177E-03 -.3815822E-04
-.8359094E-03 -.1788261E-02 -.2690521E-02 -.3636459E-02 -.4751917E-02
-.5712509E-02 -.6594045E-02 -.7534314E-02 -.8285085E-02 -.8825090E-02
-.9027546E-02 -.8920334E-02 -.8616465E-02 -.8209239E-02 -.7895718E-02
-.7546820E-02 -.7155055E-02 -.6639884E-02 -.5760295E-02 -.4302479E-02
-.2278525E-02 .1790444E-03 .2898006E-02 .5379946E-02 .7395191E-02
.8695836E-02 .9344333E-02 .9681676E-02 .9732507E-02 .9837005E-02
.1003824E-01 .1015341E-01 .1022496E-01 .1000068E-01 .9404290E-02
.8718918E-02 .7965965E-02 .7139967E-02 .6447437E-02 .5798382E-02
.5082355E-02 .4318732E-02 .3301407E-02 .1877672E-02 -.6846301E-04
-.2643313E-02 -.5544712E-02 -.8370909E-02 -.1083761E-01 -.1271198E-01
-.1399774E-01 -.1479195E-01 -.1529283E-01 -.1580039E-01 -.1647093E-01
-.1724617E-01 -.1791320E-01 -.1835852E-01 -.1849898E-01 -.1815983E-01
-.1740111E-01 -.1643913E-01 -.1535994E-01 -.1430412E-01 -.1336712E-01
-.1246845E-01 -.1155704E-01 -.1060923E-01 -.9638327E-02 -.8656718E-02
-.7666105E-02 -.6681173E-02 -.5752546E-02 -.4931658E-02 -.4272852E-02
-.3870866E-02 -.3667222E-02 -.3617013E-02 -.3704612E-02 -.3770654E-02
-.3718759E-02 -.3655301E-02 -.3411307E-02 -.2840159E-02 -.2206053E-02
-.1487967E-02 -.6835307E-03 .9757925E-04 .8886143E-03 .1693404E-02
.2446109E-02 .3161164E-02 .3980987E-02 .4809609E-02 .5431513E-02
.5752881E-02 .5650435E-02 .5032142E-02 .3868185E-02 .2294582E-02
.6196050E-03 -.9887878E-03 -.2556106E-02 -.4106610E-02 -.5691645E-02
-.7332418E-02 -.8953705E-02 -.1059824E-01 -.1221766E-01 -.1360945E-01
-.1459371E-01 -.1494915E-01 -.1461667E-01 -.1387553E-01 -.1285067E-01
-.1172563E-01 -.1076229E-01 -.9815928E-02 -.8952319E-02 -.8155768E-02
-.7227701E-02 -.6300747E-02 -.5377636E-02 -.4484430E-02 -.3589152E-02
-.2671803E-02 -.1892406E-02 -.1054205E-02 -.1201819E-03 .7704291E-03
.1632170E-02 .2526696E-02 .3426523E-02 .4181529E-02 .5044319E-02
.5875677E-02 .6575618E-02 .7400322E-02 .8108606E-02 .8789829E-02
.9514649E-02 .1006948E-01 .1036924E-01 .1035038E-01 .9992362E-02
.9336564E-02 .8514637E-02 .7772245E-02 .7086018E-02 .6330743E-02
.5802475E-02 .5285529E-02 .4688325E-02 .4336251E-02 .3978414E-02
.3639463E-02 .3396644E-02 .3074477E-02 .2787161E-02 .2511388E-02
.2205691E-02 .1943480E-02 .1667270E-02 .1387161E-02 .1148147E-02
.8478166E-03 .5463956E-03 .3801887E-03 .1880852E-03 .1437325E-03
.3579065E-03 .8096251E-03 .1510281E-02 .2254199E-02 .2947614E-02
.3376264E-02 .3449274E-02 .2978388E-02 .1971365E-02 .8025815E-03
-.4945727E-03 -.1777486E-02 -.2912057E-02 -.4025415E-02 -.5113880E-02
-.6114851E-02 -.7037254E-02 -.7985605E-02 -.8774682E-02 -.9440006E-02
-.1019836E-01 -.1088072E-01 -.1158510E-01 -.1238042E-01 -.1306384E-01
-.1371476E-01 -.1448139E-01 -.1514874E-01 -.1575684E-01 -.1638967E-01
-.1690445E-01 -.1741117E-01 -.1795520E-01 -.1848447E-01 -.1900292E-01
-.1950628E-01 -.2005206E-01 -.2061885E-01 -.2107893E-01 -.2133895E-01
-.2135737E-01 -.2100709E-01 -.2014016E-01 -.1895980E-01 -.1767444E-01
-.1630400E-01 -.1495259E-01 -.1374251E-01 -.1251072E-01 -.1122870E-01
-.1001319E-01 -.8738274E-02 -.7539443E-02 -.6492908E-02 -.5519300E-02
-.4694943E-02 -.3970272E-02 -.3286336E-02 -.2520465E-02 -.1748277E-02
-.1022217E-02 -.2091931E-03 .5724945E-03 .1327017E-02 .2074191E-02
.2838011E-02 .3598866E-02 .4373636E-02 .5175845E-02 .5918087E-02
.6690399E-02 .7514947E-02 .8289509E-02 .9013026E-02 .9841878E-02
.1068634E-01 .1144395E-01 .1221876E-01 .1296432E-01 .1370129E-01
.1444306E-01 .1513911E-01 .1589179E-01 .1667051E-01 .1740415E-01
.1817201E-01 .1894096E-01 .1971361E-01 .2051700E-01 .2131862E-01
.2205118E-01 .2274641E-01 .2343906E-01 .2407451E-01 .2470104E-01
.2529072E-01 .2593011E-01 .2659463E-01 .2723364E-01 .2786906E-01
.2848375E-01 .2910891E-01 .2963695E-01 .3000056E-01 .2995937E-01
.2951968E-01 .2888872E-01 .2799730E-01 .2700131E-01 .2607613E-01
.2520082E-01 .2433173E-01 .2355243E-01 .2275422E-01 .2179665E-01
.2091438E-01 .2000954E-01 .1896054E-01 .1789333E-01 .1672851E-01
.1553590E-01 .1438577E-01 .1315009E-01 .1191806E-01 .1073388E-01
.9582577E-02 .8481141E-02 .7482894E-02 .6801505E-02 .6427535E-02
.6316712E-02 .6416042E-02 .6511312E-02 .6613763E-02 .6761348E-02
.6778063E-02 .6754865E-02 .6793810E-02 .6827682E-02 .6888210E-02
.6916816E-02 .6950606E-02 .6981167E-02 .6993561E-02 .7042773E-02
.7046740E-02 .7059720E-02 .7114555E-02 .7161510E-02 .7167308E-02
.7021681E-02 .6649175E-02 .6064092E-02 .5291766E-02 .4406959E-02
.3457435E-02 .2510584E-02 .1687596E-02 .9352894E-03 .3782349E-03
.1257357E-03 .1212414E-03 .4098958E-03 .8233052E-03 .1250195E-02
.1674710E-02 .2032924E-02 .2381777E-02 .2719313E-02 .3040589E-02
.3340891E-02 .3516452E-02 .3454116E-02 .3134428E-02 .2638280E-02
.1988043E-02 .1291571E-02 .6513633E-03 .4115712E-04 -.5110257E-03
-.9729956E-03 -.1252806E-02 -.1283499E-02 -.1028184E-02 -.5427602E-03
.9048501E-04 .8000865E-03 .1351701E-02 .1608437E-02 .1422218E-02
.5642980E-03 -.8702073E-03 -.2665578E-02 -.4659463E-02 -.6645737E-02
-.8581648E-02 -.1042527E-01 -.1223105E-01 -.1400749E-01 -.1562581E-01
-.1694479E-01 -.1765803E-01 -.1777538E-01 -.1742752E-01 -.1671990E-01
-.1593158E-01 -.1524717E-01 -.1464345E-01 -.1407914E-01 -.1355395E-01
-.1296656E-01 -.1226515E-01 -.1148702E-01 -.1071789E-01 -.9963704E-02
-.9108515E-02 -.8094684E-02 -.6900508E-02 -.5413114E-02 -.3639058E-02
-.1773502E-02 .9424512E-04 .1962628E-02 .3710333E-02 .5243191E-02
.6386772E-02 .6831027E-02 .6553389E-02 .5645105E-02 .4306550E-02
.2871868E-02 .1525925E-02 .2722562E-03 -.8360054E-03 -.1739573E-02
-.2460971E-02 -.2890240E-02 -.2975844E-02 -.2823912E-02 -.2576770E-02
-.2300651E-02 -.2067811E-02 -.1898546E-02 -.1851372E-02 -.2084421E-02
-.2579161E-02 -.3336360E-02 -.4353439E-02 -.5507340E-02 -.6728171E-02
-.7971964E-02 -.9286932E-02 -.1077157E-01 -.1236036E-01 -.1379893E-01
-.1478611E-01 -.1501493E-01 -.1427981E-01 -.1275392E-01 -.1087813E-01
-.8980823E-02 -.7400696E-02 -.6455729E-02 -.6091517E-02 -.6098226E-02
-.6289263E-02 -.6479012E-02 -.6536858E-02 -.6364211E-02 -.5797897E-02
-.4745136E-02 -.3211062E-02 -.1275368E-02 .7720732E-03 .2704702E-02
.4414194E-02 .5800065E-02 .6879298E-02 .7692018E-02 .8150432E-02
.8205491E-02 .7975042E-02 .7542872E-02 .6995942E-02 .6475484E-02
.5984036E-02 .5545801E-02 .5125410E-02 .4596901E-02 .3936358E-02
.2992405E-02 .1756064E-02 .2768444E-03 -.1385962E-02 -.3060575E-02
-.4692069E-02 -.6210452E-02 -.7548923E-02 -.8608174E-02 -.9324367E-02
-.9743044E-02 -.9966012E-02 -.1007410E-01 -.1016823E-01 -.1032774E-01
-.1048900E-01 -.1067007E-01 -.1086707E-01 -.1102498E-01 -.1116417E-01
-.1130411E-01 -.1145015E-01 -.1156416E-01 -.1161813E-01 -.1151396E-01
-.1116910E-01 -.1062412E-01 -.9873373E-02 -.8987641E-02 -.8123078E-02
-.7252574E-02 -.6277929E-02 -.5139615E-02 -.3758278E-02 -.2108786E-02
-.3037877E-03 .1512916E-02 .3246851E-02 .4825347E-02 .6165732E-02
.7053946E-02 .7407015E-02 .7306948E-02 .6824108E-02 .6126497E-02
.5395605E-02 .4735706E-02 .4145562E-02 .3582880E-02 .3010571E-02
.2429016E-02 .1852722E-02 .1268032E-02 .7790853E-03 .4163089E-03
.1241426E-03 -.8256823E-04 -.2676551E-03 -.4565719E-03 -.6765935E-03
-.9578543E-03 -.1333950E-02 -.1929485E-02 -.2799383E-02 -.3998797E-02
-.5411238E-02 -.6809948E-02 -.8094199E-02 -.9140870E-02 -.9939898E-02
-.1059492E-01 -.1120225E-01 -.1184145E-01 -.1246029E-01 -.1296932E-01
-.1328528E-01 -.1330266E-01 -.1300279E-01 -.1249487E-01 -.1195061E-01
-.1142438E-01 -.1091830E-01 -.1046579E-01 -.1002865E-01 -.9516446E-02
-.8880209E-02 -.8162702E-02 -.7381981E-02 -.6569535E-02 -.5844293E-02
-.5332524E-02 -.5127164E-02 -.5230984E-02 -.5580506E-02 -.6065199E-02
-.6576555E-02 -.7055518E-02 -.7483169E-02 -.7857245E-02 -.8210891E-02
-.8549536E-02 -.8784769E-02 -.8908805E-02 -.8965009E-02 -.8953785E-02
-.8890542E-02 -.8812452E-02 -.8770742E-02 -.8713877E-02 -.8496546E-02
-.8073468E-02 -.7415896E-02 -.6556235E-02 -.5609972E-02 -.4620983E-02
-.3651461E-02 -.2776462E-02 -.2047861E-02 -.1570904E-02 -.1433280E-02
-.1627593E-02 -.2091562E-02 -.2670573E-02 -.3159750E-02 -.3295051E-02
-.2841774E-02 -.1722596E-02 -.3081415E-04 .2012427E-02 .4195846E-02
.7759243E-02 .1009041E-01 .1234082E-01 .1430464E-01 .1580129E-01
.1664470E-01 .1683972E-01 .1664863E-01 .1637586E-01 .1633187E-01
.1673473E-01 .1755643E-01 .1865395E-01 .1977178E-01 .2065249E-01
.2119936E-01 .2144359E-01 .2154501E-01 .2162092E-01 .2175057E-01
.2198130E-01 .2222906E-01 .2246085E-01 .2266158E-01 .2281945E-01
.2299220E-01 .2326148E-01 .2368028E-01 .2424810E-01 .2493331E-01
.2567153E-01 .2640765E-01 .2709228E-01 .2770083E-01 .2828892E-01
.2888795E-01 .2944563E-01 .2997140E-01 .3044045E-01 .3067170E-01
.3057340E-01 .3011340E-01 .2927922E-01 .2827632E-01 .2726479E-01
.2618787E-01 .2508466E-01 .2394896E-01 .2263012E-01 .2115497E-01
.1961779E-01 .1812645E-01 .1682319E-01 .1590214E-01 .1546219E-01
.1538958E-01 .1553484E-01 .1573604E-01 .1587445E-01 .1580009E-01
.1544133E-01 .1481476E-01 .1392805E-01 .1286946E-01 .1175946E-01
.1068899E-01 .9637313E-02 .8571257E-02 .7508245E-02 .6396172E-02
.5176293E-02 .3871487E-02 .2513818E-02 .1133142E-02 -.2125730E-03
-.1472745E-02 -.2621101E-02 -.3570533E-02 -.4262023E-02 -.4737111E-02
-.5092072E-02 -.5395414E-02 -.5679843E-02 -.6017004E-02 -.6386026E-02
-.6787382E-02 -.7300432E-02 -.7967211E-02 -.8808489E-02 -.9845551E-02
-.1098767E-01 -.1203648E-01 -.1285782E-01 -.1330239E-01 -.1328962E-01
-.1295417E-01 -.1244764E-01 -.1186071E-01 -.1131595E-01 -.1089068E-01
-.1059892E-01 -.1049736E-01 -.1060687E-01 -.1094933E-01 -.1152811E-01
-.1220358E-01 -.1289204E-01 -.1357616E-01 -.1423100E-01 -.1482947E-01
-.1538425E-01 -.1592233E-01 -.1636338E-01 -.1656591E-01 -.1643236E-01
-.1597662E-01 -.1527350E-01 -.1443529E-01 -.1357836E-01 -.1278392E-01
-.1206561E-01 -.1137277E-01 -.1071076E-01 -.1017280E-01 -.9799921E-02
-.9649394E-02 -.9757075E-02 -.1004074E-01 -.1042480E-01 -.1079276E-01
-.1109372E-01 -.1136999E-01 -.1160094E-01 -.1178761E-01 -.1197894E-01
-.1207333E-01 -.1193329E-01 -.1155171E-01 -.1093361E-01 -.1012867E-01
-.9261098E-02 -.8423422E-02 -.7678327E-02 -.7119483E-02 -.6791939E-02
-.6712115E-02 -.6843079E-02 -.7089660E-02 -.7379934E-02 -.7649899E-02
-.7845475E-02 -.7915957E-02 -.7819283E-02 -.7499915E-02 -.6994798E-02
-.6395132E-02 -.5723564E-02 -.5048186E-02 -.4439030E-02 -.3859009E-02
-.3285826E-02 -.2720984E-02 -.2147649E-02 -.1564056E-02 -.9720655E-03
-.3924482E-03 .1650661E-03 .7174124E-03 .1257095E-02 .1789369E-02
.2359872E-02 .2957085E-02 .3528682E-02 .4030079E-02 .4409755E-02
.4610036E-02 .4614296E-02 .4491448E-02 .4327655E-02 .4181567E-02
.4104591E-02 .4130779E-02 .4265152E-02 .4465588E-02 .4693861E-02
.4931583E-02 .5140223E-02 .5326284E-02 .5517676E-02 .5702954E-02
.5880905E-02 .6067302E-02 .6213250E-02 .6267491E-02 .6210680E-02
.6029982E-02 .5763171E-02 .5463723E-02 .5173041E-02 .4898239E-02
.4639396E-02 .4397928E-02 .4151844E-02 .3904857E-02 .3663023E-02
.3445871E-02 .3303808E-02 .3251068E-02 .3289394E-02 .3397992E-02
.3510975E-02 .3548474E-02 .3458961E-02 .3214734E-02 .2803493E-02
.2291440E-02 .1774944E-02 .1277190E-02 .7986186E-03 .3663494E-03
-.3482560E-04 -.4354777E-03 -.8270837E-03 -.1209550E-02 -.1576519E-02
-.1914450E-02 -.2243036E-02 -.2553874E-02 -.2851191E-02 -.3150080E-02
-.3432364E-02 -.3706000E-02 -.3985497E-02 -.4265150E-02 -.4527991E-02
-.4767252E-02 -.4964962E-02 -.5061370E-02 -.5023966E-02 -.4868896E-02
-.4625376E-02 -.4352955E-02 -.4107218E-02 -.3938803E-02 -.3873476E-02
-.3905834E-02 -.4019462E-02 -.4164923E-02 -.4317415E-02 -.4467577E-02
-.4621323E-02 -.4794882E-02 -.4987393E-02 -.5198910E-02 -.5387553E-02
-.5502042E-02 -.5509470E-02 -.5372261E-02 -.5121201E-02 -.4810435E-02
-.4475207E-02 -.4173282E-02 -.3924784E-02 -.3747185E-02 -.3661682E-02
-.3644232E-02 -.3678184E-02 -.3736803E-02 -.3780249E-02 -.3787912E-02
-.3723627E-02 -.3553096E-02 -.3274377E-02 -.2914601E-02 -.2510209E-02
-.2101883E-02 -.1725180E-02 -.1395088E-02 -.1110167E-02 -.8630921E-03
-.6677471E-03 -.5430111E-03 -.4917591E-03 -.5032961E-03 -.5512626E-03
-.6093181E-03 -.6585218E-03 -.6917868E-03 -.7160987E-03 -.7341155E-03
-.7577296E-03 -.7942437E-03 -.8357234E-03 -.8798277E-03 -.9201046E-03
-.9494655E-03 -.9693090E-03 -.9821635E-03 -.9870876E-03 -.9832932E-03
-.9688699E-03 -.9330460E-03 -.8579200E-03 -.7386728E-03 -.5812859E-03
-.3997294E-03 -.2121420E-03 -.3243040E-04 .1293954E-03 .2772012E-03
.4166618E-03 .5497497E-03 .6796009E-03 .8076697E-03 .9376777E-03
.1074204E-02 .1216486E-02 .1357106E-02 .1494193E-02 .1621883E-02
.1732130E-02 .1829667E-02 .1911760E-02 .1975442E-02 .2025713E-02
.2061568E-02 .2084805E-02 .2099837E-02 .2107158E-02 .2109271E-02
.2105125E-02 .2094110E-02 .2077210E-02 .2051453E-02 .2012648E-02
.1957224E-02 .1882890E-02 .1792851E-02 .1696231E-02 .1598098E-02
.1503724E-02 .1416341E-02 .1333911E-02 .1256371E-02 .1182076E-02
.1109747E-02 .1040603E-02 .9743627E-03 .9119862E-03 .8541323E-03
.7998095E-03 .7486704E-03 .7007959E-03 .6561052E-03 .6137506E-03
.5743657E-03 .5383776E-03 .5055391E-03 .4752999E-03 .4477320E-03
.4230737E-03 .4005037E-03 .3795673E-03 .3579860E-03 .3348679E-03
.3105352E-03 .2860160E-03 .2652565E-03 .2503434E-03 .2422912E-03
.2417031E-03 .2476960E-03 .2604719E-03 .2802572E-03 .3068705E-03
.3386556E-03 .3722990E-03 .4052977E-03 .4356823E-03 .4622546E-03
.4852438E-03 .5057260E-03 .5246352E-03 .5425639E-03 .5599601E-03
.5767023E-03 .5925052E-03 .6072328E-03 .6208209E-03 .6330554E-03
.6435786E-03 .6521876E-03 .6587709E-03 .6634079E-03 .6663115E-03
.6677464E-03 .6680027E-03 .6673014E-03 .6658006E-03 .6636009E-03

View file

@ -0,0 +1,144 @@
# Eigen analysis of a two-storey one-bay frame; Example 10.5 from "Dynamics of Structures" book by Anil Chopra
# units: kips, in, sec
# Vesna Terzic, 2010
#delete all previosly constructed objects
wipe;
#set input variables
#--------------------
#mass
set m [expr 100.0/386.0]
#number of modes
set numModes 2
#material
set A 63.41
set I 320.0
set E 29000.0
#geometry
set L 240.
set h 120.
# create data directory
file mkdir modes;
# define the model
#---------------------------------
#model builder
model BasicBuilder -ndm 2 -ndf 3
# nodal coordinates:
node 1 0. 0. ;
node 2 $L 0. ;
node 3 0. $h ;
node 4 $L $h ;
node 5 0. [expr 2*$h];
node 6 $L [expr 2*$h];
# Single point constraints -- Boundary Conditions
fix 1 1 1 1;
fix 2 1 1 1;
# assign mass
mass 3 $m 0. 0. ;
mass 4 $m 0. 0. ;
mass 5 [expr $m/2.] 0. 0. ;
mass 6 [expr $m/2.] 0. 0. ;
# define geometric transformation:
set TransfTag 1;
geomTransf Linear $TransfTag ;
# define elements:
# columns
element elasticBeamColumn 1 1 3 $A $E [expr 2.*$I] $TransfTag;
element elasticBeamColumn 2 3 5 $A $E $I $TransfTag;
element elasticBeamColumn 3 2 4 $A $E [expr 2.*$I] $TransfTag;
element elasticBeamColumn 4 4 6 $A $E $I $TransfTag;
# beams
element elasticBeamColumn 5 3 4 $A $E [expr 2.*$I] $TransfTag;
element elasticBeamColumn 6 5 6 $A $E $I $TransfTag;
# record eigenvectors
#----------------------
for { set k 1 } { $k <= $numModes } { incr k } {
recorder Node -file [format "modes/mode%i.out" $k] -nodeRange 1 6 -dof 1 2 3 "eigen $k"
}
# perform eigen analysis
#-----------------------------
set lambda [eigen $numModes];
# calculate frequencies and periods of the structure
#---------------------------------------------------
set omega {}
set f {}
set T {}
set pi 3.141593
foreach lam $lambda {
lappend omega [expr sqrt($lam)]
lappend f [expr sqrt($lam)/(2*$pi)]
lappend T [expr (2*$pi)/sqrt($lam)]
}
puts "periods are $T"
# write the output file cosisting of periods
#--------------------------------------------
set period "modes/Periods.txt"
set Periods [open $period "w"]
foreach t $T {
puts $Periods " $t"
}
close $Periods
# record the eigenvectors
#------------------------
record
# create display for mode shapes
#---------------------------------
# $windowTitle $xLoc $yLoc $xPixels $yPixels
recorder display "Mode Shape 1" 10 10 500 500 -wipe
prp $h $h 1; # projection reference point (prp); defines the center of projection (viewer eye)
vup 0 1 0; # view-up vector (vup)
vpn 0 0 1; # view-plane normal (vpn)
viewWindow -200 200 -200 200; # coordiantes of the window relative to prp
display -1 5 20; # the 1st arg. is the tag for display mode (ex. -1 is for the first mode shape)
# the 2nd arg. is magnification factor for nodes, the 3rd arg. is magnif. factor of deformed shape
recorder display "Mode Shape 2" 10 510 500 500 -wipe
prp $h $h 1;
vup 0 1 0;
vpn 0 0 1;
viewWindow -200 200 -200 200
display -2 5 20
# get values of eigenvectors for translational DOFs
#---------------------------------------------------
set f11 [nodeEigenvector 3 1 1]
set f21 [nodeEigenvector 5 1 1]
set f12 [nodeEigenvector 3 2 1]
set f22 [nodeEigenvector 5 2 1]
puts "eigenvector 1: [list [expr {$f11/$f21}] [expr {$f21/$f21}] ]"
puts "eigenvector 2: [list [expr {$f12/$f22}] [expr {$f22/$f22}] ]"

View file

@ -0,0 +1,167 @@
# Eigen analysis of a two-storey shear frame; Example 10.4 from "Dynamics of Structures" book by Anil Chopra - using equalDOF and very high Ib
# units: in, kips
wipe
#input
set m [expr 100.0/386.0]
set numModes 2
#material
set Ac 63.41
set Ic 320.0
set E 30000.0
set Ib 10e+12
set Ab 63.41
#geometry
set L 288.
set h 144.
# create data directory
file mkdir modes;
# define the model
#---------------------------------
#model builder
model BasicBuilder -ndm 2 -ndf 3
# nodal coordinates:
node 1 0. 0. ;
node 2 $L 0. ;
node 3 0. $h ;
node 4 $L $h ;
node 5 0. [expr 2*$h];
node 6 $L [expr 2*$h];
# Single point constraints -- Boundary Conditions
fix 1 1 1 1;
fix 2 1 1 1;
# MP constraints
equalDOF 3 4 2 3
equalDOF 5 6 2 3
# assign mass
mass 3 $m 0. 0. ;
mass 4 $m 0. 0. ;
mass 5 [expr $m/2.] 0. 0. ;
mass 6 [expr $m/2.] 0. 0. ;
# define geometric transformation:
set TransfTag 1;
geomTransf Linear $TransfTag ;
# define elements:
# columns
element elasticBeamColumn 1 1 3 $Ac $E [expr 2.*$Ic] $TransfTag;
element elasticBeamColumn 2 3 5 $Ac $E $Ic $TransfTag;
element elasticBeamColumn 3 2 4 $Ac $E [expr 2.*$Ic] $TransfTag;
element elasticBeamColumn 4 4 6 $Ac $E $Ic $TransfTag;
# beams
element elasticBeamColumn 5 3 4 $Ab $E $Ib $TransfTag;
element elasticBeamColumn 6 5 6 $Ab $E $Ib $TransfTag;
# record eigenvectors
#----------------------
for { set k 1 } { $k <= $numModes } { incr k } {
recorder Node -file [format "modes/mode%i.out" $k] -nodeRange 1 6 -dof 1 2 3 "eigen $k"
}
# perform eigen analysis
#-----------------------------
set lambda [eigen $numModes];
# calculate frequencies and periods of the structure
#---------------------------------------------------
set omega {}
set f {}
set T {}
set pi 3.141593
foreach lam $lambda {
lappend omega [expr sqrt($lam)]
lappend f [expr sqrt($lam)/(2*$pi)]
lappend T [expr (2*$pi)/sqrt($lam)]
}
puts "periods are $T"
# write the output file cosisting of periods
#--------------------------------------------
set period "modes/Periods.txt"
set Periods [open $period "w"]
foreach t $T {
puts $Periods " $t"
}
close $Periods
# create display for mode shapes
#---------------------------------
# $windowTitle $xLoc $yLoc $xPixels $yPixels
recorder display "Mode Shape 1" 10 10 500 500 -wipe
prp $h $h 1; # projection reference point (prp); defines the center of projection (viewer eye)
vup 0 1 0; # view-up vector (vup)
vpn 0 0 1; # view-plane normal (vpn)
viewWindow -200 200 -200 200; # coordiantes of the window relative to prp
display -1 5 20; # the 1st arg. is the tag for display mode (ex. -1 is for the first mode shape)
# the 2nd arg. is magnification factor for nodes, the 3rd arg. is magnif. factor of deformed shape
recorder display "Mode Shape 2" 10 510 500 500 -wipe
prp $h $h 1;
vup 0 1 0;
vpn 0 0 1;
viewWindow -200 200 -200 200
display -2 5 20
# Run a one step gravity load with no loading (to record eigenvectors)
#-----------------------------------------------------------------------
integrator LoadControl 0 1 0 0
# Convergence test
# tolerance maxIter displayCode
test EnergyIncr 1.0e-10 100 0
# Solution algorithm
algorithm Newton
# DOF numberer
numberer RCM
# Constraint handler
constraints Transformation
# System of equations solver
system ProfileSPD
analysis Static
set res [analyze 1]
if {$res < 0} {
puts "Modal analysis failed"
}
# get values of eigenvectors for translational DOFs
#---------------------------------------------------
set f11 [nodeEigenvector 3 1 1]
set f21 [nodeEigenvector 5 1 1]
set f12 [nodeEigenvector 3 2 1]
set f22 [nodeEigenvector 5 2 1]
puts "eigenvector 1: [list [expr {$f11/$f21}] [expr {$f21/$f21}] ]"
puts "eigenvector 2: [list [expr {$f12/$f22}] [expr {$f22/$f22}] ]"

View file

@ -0,0 +1,95 @@
# --------------------------------------------------------------------------------------------------
# Example 1. cantilever 2D
# EQ ground motion with gravity
# all units are in kip, inch, second
# elasticBeamColumn ELEMENT
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) 36'
# | |
# | |
# | |
# =1= ---- -------->X
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear opensees model
model basic -ndm 2 -ndf 3; # 2 dimensions, 3 dof per node
file mkdir data; # create data directory
# define GEOMETRY -------------------------------------------------------------
# nodal coordinates:
node 1 0. 0.; # node#, X Y
node 2 0. 432.
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 5.18 0. 0.; # node#, Mx My Mz, Mass=Weight/g.
# Define ELEMENTS -------------------------------------------------------------
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
geomTransf Linear 1; # associate a tag to transformation
# connectivity:
element elasticBeamColumn 1 1 2 3600 3225 1080000 1; # element elasticBeamColumn $eleTag $iNode $jNode $A $E $Iz $transfTag
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 force; # element forces -- column
# define GRAVITY -------------------------------------------------------------
timeSeries Linear 1
pattern Plain 1 1 {
load 2 0. -2000. 0.; # node#, FX FY MZ -- superstructure-weight
}
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
algorithm Linear; # use Linear algorithm for linear analysis
integrator LoadControl 0.1; # determine the next time step for an analysis, # apply gravity in 10 steps
analysis Static # define type of analysis static or transient
analyze 10; # perform gravity analysis
loadConst -time 0.0; # hold gravity constant and restart time
# DYNAMIC ground-motion analysis -------------------------------------------------------------
# create load pattern
set G 386
timeSeries Path 2 -dt 0.005 -filePath A10000.tcl -factor $G; # define acceleration vector from file (dt=0.005 is associated with the input file gm)
pattern UniformExcitation 2 1 -accel 2; # define where and how (pattern tag, dof) acceleration is applied
# set damping based on first eigen mode
set freq [expr [eigen -fullGenLapack 1]**0.5]
set dampRatio 0.02
rayleigh 0. 0. 0. [expr 2*$dampRatio/$freq]
# display displacement shape of the column
recorder display "Displaced shape" 10 10 500 500 -wipe
prp 200. 50. 1;
vup 0 1 0;
vpn 0 0 1;
display 1 5 40
# create the analysis
wipeAnalysis; # clear previously-define analysis parameters
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
algorithm Linear # use Linear algorithm for linear analysis
integrator Newmark 0.5 0.25 ; # determine the next time step for an analysis
analysis Transient; # define type of analysis: time-dependent
analyze 3995 0.01; # apply 3995 0.01-sec time steps in analysis
puts "Done!"
wipe

View file

@ -0,0 +1,86 @@
# --------------------------------------------------------------------------------------------------
# Example 1. cantilever 2D
# EQ ground motion with gravity
# all units are in kip, inch, second
# elasticBeamColumn ELEMENT
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) 36'
# | |
# | |
# | |
# =1= ---- -------->X
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear opensees model
model basic -ndm 2 -ndf 3; # 2 dimensions, 3 dof per node
file mkdir Data; # create data directory
# define GEOMETRY -------------------------------------------------------------
# nodal coordinates:
node 1 0 0; # node#, X Y
node 2 0 432
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 5.18 1.e-9 0.; # node#, Mx My Mz, Mass=Weight/g.
# Define ELEMENTS -------------------------------------------------------------
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
geomTransf Linear 1; # associate a tag to transformation
# connectivity: (make A very large, 10e6 times its actual value)
element elasticBeamColumn 1 1 2 3600000000 4227 1080000 1; # element elasticBeamColumn $eleTag $iNode $jNode $A $E $Iz $transfTag
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 globalForce; # element forces -- column
recorder Element -file Data/DCol.out -time -ele 1 deformations; # element deformations -- column
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0. -2000. 0.; # node#, FX FY MZ -- superstructure-weight
}
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr 1.0e-8 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
integrator LoadControl 0.1; # determine the next time step for an analysis, # apply gravity in 10 steps
analysis Static # define type of analysis static or transient
analyze 10; # perform gravity analysis
loadConst -time 0.0; # hold gravity constant and restart time
# DYNAMIC ground-motion analysis -------------------------------------------------------------
# create load pattern
set accelSeries "Series -dt 0.01 -filePath BM68elc.acc -factor 1"; # define acceleration vector from file (dt=0.01 is associated with the input file gm)
pattern UniformExcitation 2 1 -accel $accelSeries; # define where and how (pattern tag, dof) acceleration is applied
rayleigh 0. 0. 0. [expr 2*0.02/pow([eigen 1],0.5)]; # set damping based on first eigen mode
# create the analysis
wipeAnalysis; # clear previously-define analysis parameters
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr 1.0e-8 10; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
integrator Newmark 0.5 0.25 ; # determine the next time step for an analysis
analysis Transient; # define type of analysis: time-dependent
analyze 1000 0.02; # apply 1000 0.02-sec time steps in analysis
puts "Done!"

View file

@ -0,0 +1,80 @@
# --------------------------------------------------------------------------------------------------
# Example 1. cantilever 2D
# static pushover analysis with gravity.
# all units are in kip, inch, second
# elasticBeamColumn ELEMENT
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) 36'
# | |
# | |
# | |
# =1= ---- -------->X
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear opensees model
model basic -ndm 2 -ndf 3; # 2 dimensions, 3 dof per node
file mkdir Data; # create data directory
# define GEOMETRY -------------------------------------------------------------
# nodal coordinates:
node 1 0 0; # node#, X Y
node 2 0 432
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 5.18 0. 0.; # node#, Mx My Mz, Mass=Weight/g.
# Define ELEMENTS -------------------------------------------------------------
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
geomTransf Linear 1; # associate a tag to transformation
# connectivity: (make A very large, 10e6 times its actual value)
element elasticBeamColumn 1 1 2 3600000000 4227 1080000 1; # element elasticBeamColumn $eleTag $iNode $jNode $A $E $Iz $transfTag
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 globalForce; # element forces -- column
recorder Element -file Data/DCol.out -time -ele 1 deformation; # element deformations -- column
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0. -2000. 0.; # node#, FX FY MZ -- superstructure-weight
}
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr 1.0e-8 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
integrator LoadControl 0.1; # determine the next time step for an analysis, # apply gravity in 10 steps
analysis Static # define type of analysis static or transient
analyze 10; # perform gravity analysis
loadConst -time 0.0; # hold gravity constant and restart time
# define LATERAL load -------------------------------------------------------------
# Lateral load pattern
pattern Plain 2 Linear {
load 2 2000. 0.0 0.0; # node#, FX FY MZ -- representative lateral load at top node
}
# pushover: diplacement controlled static analysis
integrator DisplacementControl 2 1 0.1; # switch to displacement control, for node 11, dof 1, 0.1 increment
analyze 1000; # apply 100 steps of pushover analysis to a displacement of 10
puts "Done!"

View file

@ -0,0 +1,95 @@
# --------------------------------------------------------------------------------------------------
# Example 1. portal frame in 2D
# dynamic earthquake analysis of Portal Frame, with gravity.
# all units are in kip, inch, second
# elasticBeamColumn ELEMENT
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 3_________(3)________4 __
# | | |
# | | |
# | | |
# (1) (2) LCol
# | | |
# | | |
# | | |
# =1= =2= _|_ -------->X
# |----------LBeam------------|
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear opensees model
model basic -ndm 2 -ndf 3; # 2 dimensions, 3 dof per node
file mkdir Data; # create data directory
# define GEOMETRY -------------------------------------------------------------
# nodal coordinates:
node 1 0 0; # node#, X Y
node 2 504 0
node 3 0 432
node 4 504 432
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
fix 2 1 1 1; # node DX DY RZ
fix 3 0 0 0
fix 4 0 0 0
# nodal masses:
mass 3 5.18 0. 0.; # node#, Mx My Mz, Mass=Weight/g.
mass 4 5.18 0. 0.
# Define ELEMENTS -------------------------------------------------------------
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
geomTransf Linear 1; # associate a tag to transformation
# connectivity: (make A very large, 10e6 times its actual value)
element elasticBeamColumn 1 1 3 3600000000 4227 1080000 1; # element elasticBeamColumn $eleTag $iNode $jNode $A $E $Iz $transfTag
element elasticBeamColumn 2 2 4 3600000000 4227 1080000 1
element elasticBeamColumn 3 3 4 5760000000 4227 4423680 1
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 3 4 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 2 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 2 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 2 -jNode 3 4 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 2 globalForce; # element forces -- column
recorder Element -file Data/FBeam.out -time -ele 3 globalForce; # element forces -- beam
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
eleLoad -ele 3 -type -beamUniform -7.94 ; # distributed superstructure-weight on beam
}
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr 1.0e-8 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
integrator LoadControl 0.1; # determine the next time step for an analysis, # apply gravity in 10 steps
analysis Static # define type of analysis static or transient
analyze 10; # perform gravity analysis
loadConst -time 0.0; # hold gravity constant and restart time
# DYNAMIC ground-motion analysis -------------------------------------------------------------
# create load pattern
set accelSeries "Series -dt 0.01 -filePath BM68elc.acc -factor 1"; # define acceleration vector from file (dt=0.01 is associated with the input file gm)
pattern UniformExcitation 2 1 -accel $accelSeries; # define where and how (pattern tag, dof) acceleration is applied
rayleigh 0. 0. 0. [expr 2*0.02/pow([eigen 1],0.5)]; # set damping based on first eigen mode
# create the analysis
wipeAnalysis; # clear previously-define analysis parameters
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr 1.0e-8 10; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
integrator Newmark 0.5 0.25 ; # determine the next time step for an analysis
analysis Transient; # define type of analysis: time-dependent
analyze 1000 0.02; # apply 1000 0.02-sec time steps in analysis
puts "Done!"

View file

@ -0,0 +1,94 @@
# --------------------------------------------------------------------------------------------------
# Example 1. portal frame in 2D
# static pushover analysis of Portal Frame, with gravity.
# all units are in kip, inch, second
# elasticBeamColumn ELEMENT
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 3_________(3)________4 __
# | | |
# | | |
# | | |
# (1) (2) LCol
# | | |
# | | |
# | | |
# =1= =2= _|_ -------->X
# |----------LBeam------------|
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear opensees model
model basic -ndm 2 -ndf 3; # 2 dimensions, 3 dof per node
file mkdir Data; # create data directory
# define GEOMETRY -------------------------------------------------------------
# nodal coordinates:
node 1 0 0; # node#, X Y
node 2 504 0
node 3 0 432
node 4 504 432
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
fix 2 1 1 1; # node DX DY RZ
fix 3 0 0 0
fix 4 0 0 0
# nodal masses:
mass 3 5.18 0. 0.; # node#, Mx My Mz, Mass=Weight/g.
mass 4 5.18 0. 0.
# Define ELEMENTS -------------------------------------------------------------
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
geomTransf Linear 1; # associate a tag to transformation
# connectivity: (make A very large, 10e6 times its actual value)
element elasticBeamColumn 1 1 3 3600000000 4227 1080000 1; # element elasticBeamColumn $eleTag $iNode $jNode $A $E $Iz $transfTag
element elasticBeamColumn 2 2 4 3600000000 4227 1080000 1
element elasticBeamColumn 3 3 4 5760000000 4227 4423680 1
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 3 4 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 2 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 2 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 2 -jNode 3 4 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 2 globalForce; # element forces -- column
recorder Element -file Data/FBeam.out -time -ele 3 globalForce; # element forces -- beam
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
eleLoad -ele 3 -type -beamUniform -7.94 ; # distributed superstructure-weight on beam
}
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr 1.0e-8 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
integrator LoadControl 0.1; # determine the next time step for an analysis, # apply gravity in 10 steps
analysis Static # define type of analysis static or transient
analyze 10; # perform gravity analysis
loadConst -time 0.0; # hold gravity constant and restart time
# define LATERAL load -------------------------------------------------------------
# Lateral load pattern
pattern Plain 2 Linear {
load 3 2000. 0.0 0.0; # node#, FX FY MZ -- representative lateral load at top nodes
load 4 2000. 0.0 0.0; # place 1/2 of the weight for each node to get shear coefficient
}
# pushover: diplacement controlled static analysis
integrator DisplacementControl 3 1 0.1; # switch to displacement control, for node 11, dof 1, 0.1 increment
analyze 100; # apply 100 steps of pushover analysis to a displacement of 10
puts "Done!"

View file

@ -0,0 +1,231 @@
# --------------------------------------------------------------------------------------------------
# Example 2. 2D cantilever column, dynamic eq ground motion
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
# units: kip, inch, sec
wipe; # clear memory of all past model definitions
file mkdir Data; # create data directory
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
# define GEOMETRY -------------------------------------------------------------
set LCol 432; # column length
set Weight 2000; # superstructure weight
# define section geometry
set HCol 60; # Column Depth
set BCol 60; # Column Width
# calculated parameters
set PCol $Weight; # nodal dead-load weight per column
set g 386.4; # g.
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol*1000]; # cross-sectional area, make stiff
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS -------------------------------------------------------------
# Material parameters
set fc -4.; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*sqrt(-$fc*1000)]; # Concrete Elastic Modulus (the term in sqr root needs to be in psi
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
geomTransf Linear $ColTransfTag ;
# element connectivity:
element elasticBeamColumn 1 1 2 $ACol $Ec $IzCol $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 globalForce; # element forces -- column
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# ------------------------------------------------- apply gravity load
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"
# DYNAMIC EQ ANALYSIS --------------------------------------------------------
# Uniform Earthquake ground motion (uniform acceleration input at all support nodes)
set GMdirection 1; # ground-motion direction
set GMfile "BM68elc.acc" ; # ground-motion filenames
set GMfact 1.; # ground-motion scaling factor
# set up ground-motion-analysis parameters
set DtAnalysis [expr 0.01]; # time-step Dt for lateral analysis
set TmaxAnalysis [expr 10.]; # maximum duration of ground-motion analysis -- should be 50*$sec
# DYNAMIC ANALYSIS PARAMETERS
# CONSTRAINTS handler -- Determines how the constraint equations are enforced in the analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/617.htm)
# Plain Constraints -- Removes constrained degrees of freedom from the system of equations
# Lagrange Multipliers -- Uses the method of Lagrange multipliers to enforce constraints
# Penalty Method -- Uses penalty numbers to enforce constraints
# Transformation Method -- Performs a condensation of constrained degrees of freedom
constraints Transformation ;
# DOF NUMBERER (number the degrees of freedom in the domain): (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/366.htm)
# determines the mapping between equation numbers and degrees-of-freedom
# Plain -- Uses the numbering provided by the user
# RCM -- Renumbers the DOF to minimize the matrix band-width using the Reverse Cuthill-McKee algorithm
numberer Plain
# SYSTEM (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/371.htm)
# Linear Equation Solvers (how to store and solve the system of equations in the analysis)
# -- provide the solution of the linear system of equations Ku = P. Each solver is tailored to a specific matrix topology.
# ProfileSPD -- Direct profile solver for symmetric positive definite matrices
# BandGeneral -- Direct solver for banded unsymmetric matrices
# BandSPD -- Direct solver for banded symmetric positive definite matrices
# SparseGeneral -- Direct solver for unsymmetric sparse matrices (-piv option)
# SparseSPD -- Direct solver for symmetric sparse matrices
# UmfPack -- Direct UmfPack solver for unsymmetric matrices
system SparseGeneral -piv
# TEST: # convergence test to
# Convergence TEST (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/360.htm)
# -- Accept the current state of the domain as being on the converged solution path
# -- determine if convergence has been achieved at the end of an iteration step
# NormUnbalance -- Specifies a tolerance on the norm of the unbalanced load at the current iteration
# NormDispIncr -- Specifies a tolerance on the norm of the displacement increments at the current iteration
# EnergyIncr-- Specifies a tolerance on the inner product of the unbalanced load and displacement increments at the current iteration
# RelativeNormUnbalance --
# RelativeNormDispIncr --
# RelativeEnergyIncr --
set Tol 1.e-8; # Convergence Test: tolerance
set maxNumIter 10; # Convergence Test: maximum number of iterations that will be performed before "failure to converge" is returned
set printFlag 0; # Convergence Test: flag used to print information on convergence (optional) # 1: print information on each step;
set TestType EnergyIncr; # Convergence-test type
test $TestType $Tol $maxNumIter $printFlag;
# Solution ALGORITHM: -- Iterate from the last time step to the current (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/682.htm)
# Linear -- Uses the solution at the first iteration and continues
# Newton -- Uses the tangent at the current iteration to iterate to convergence
# ModifiedNewton -- Uses the tangent at the first iteration to iterate to convergence
# NewtonLineSearch --
# KrylovNewton --
# BFGS --
# Broyden --
set algorithmType ModifiedNewton
algorithm $algorithmType;
# Static INTEGRATOR: -- determine the next time step for an analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/689.htm)
# LoadControl -- Specifies the incremental load factor to be applied to the loads in the domain
# DisplacementControl -- Specifies the incremental displacement at a specified DOF in the domain
# Minimum Unbalanced Displacement Norm -- Specifies the incremental load factor such that the residual displacement norm in minimized
# Arc Length -- Specifies the incremental arc-length of the load-displacement path
# Transient INTEGRATOR: -- determine the next time step for an analysis including inertial effects
# Newmark -- The two parameter time-stepping method developed by Newmark
# HHT -- The three parameter Hilbert-Hughes-Taylor time-stepping method
# Central Difference -- Approximates velocity and acceleration by centered finite differences of displacement
set NewmarkGamma 0.5; # Newmark-integrator gamma parameter (also HHT)
set NewmarkBeta 0.25; # Newmark-integrator beta parameter
integrator Newmark $NewmarkGamma $NewmarkBeta
# ANALYSIS -- defines what type of analysis is to be performed (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/324.htm)
# Static Analysis -- solves the KU=R problem, without the mass or damping matrices.
# Transient Analysis -- solves the time-dependent analysis. The time step in this type of analysis is constant. The time step in the output is also constant.
# variableTransient Analysis -- performs the same analysis type as the Transient Analysis object. The time step, however, is variable. This method is used when
# there are convergence problems with the Transient Analysis object at a peak or when the time step is too small. The time step in the output is also variable.
analysis Transient
# define DAMPING--------------------------------------------------------------------------------------
# apply Rayleigh DAMPING from $xDamp
# D=$alphaM*M + $betaKcurr*Kcurrent + $betaKcomm*KlastCommit + $beatKinit*$Kinitial
set xDamp 0.02; # 2% damping ratio
set lambda [eigen 1]; # eigenvalue mode 1
set omega [expr pow($lambda,0.5)];
set alphaM 0.; # M-prop. damping; D = alphaM*M
set betaKcurr 0.; # K-proportional damping; +beatKcurr*KCurrent
set betaKcomm [expr 2.*$xDamp/($omega)]; # K-prop. damping parameter; +betaKcomm*KlastCommitt
set betaKinit 0.; # initial-stiffness proportional damping +beatKinit*Kini
# define damping
rayleigh $alphaM $betaKcurr $betaKinit $betaKcomm; # RAYLEIGH damping
# --------------------------------- perform Dynamic Ground-Motion Analysis
# Uniform EXCITATION: acceleration input
set IDloadTag 400; # load tag
set dt 0.01; # time step for input ground motion
set GMfatt 1.0; # data in input file is in g Unifts -- ACCELERATION TH
set AccelSeries "Series -dt $dt -filePath $GMfile -factor $GMfatt"; # time series information
pattern UniformExcitation $IDloadTag $GMdirection -accel $AccelSeries ; # create Unifform excitation
set Nsteps [expr int($TmaxAnalysis/$DtAnalysis)];
set ok [analyze $Nsteps $DtAnalysis]; # actually perform analysis; returns ok=0 if analysis was successful
if {$ok != 0} { ; # if analysis was not successful.
# change some analysis parameters to achieve convergence
# performance is slower inside this loop
# Time-controlled analysis
set ok 0;
set controlTime [getTime];
while {$controlTime < $TmaxAnalysis && $ok == 0} {
set ok [analyze 1 $DtAnalysis]
set controlTime [getTime]
set ok [analyze 1 $DtAnalysis]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 1000 0
algorithm Newton -initial
set ok [analyze 1 $DtAnalysis]
test $TestType $Tol $maxNumIter 0
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmType
}
}
}; # end if ok !0
puts "Ground Motion Done. End Time: [getTime]"

View file

@ -0,0 +1,207 @@
# --------------------------------------------------------------------------------------------------
# Example 2. 2D cantilever column, static pushover
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
# units: kip, inch, sec
wipe; # clear memory of all past model definitions
file mkdir Data; # create data directory
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
# define GEOMETRY -------------------------------------------------------------
set LCol 432; # column length
set Weight 2000; # superstructure weight
# define section geometry
set HCol 60; # Column Depth
set BCol 60; # Column Width
# calculated parameters
set PCol $Weight; # nodal dead-load weight per column
set g 386.4; # g.
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol*1000]; # cross-sectional area, make stiff
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS -------------------------------------------------------------
# Material parameters
set fc -4.; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*sqrt(-$fc*1000)]; # Concrete Elastic Modulus (the term in sqr root needs to be in psi
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
geomTransf Linear $ColTransfTag ;
# element connectivity:
element elasticBeamColumn 1 1 2 $ACol $Ec $IzCol $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 1 globalForce; # element forces -- column
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# ------------------------------------------------- apply gravity load
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"
# STATIC PUSHOVER ANALYSIS --------------------------------------------------------------------------------------------------
#
# we need to set up parameters that are particular to the model.
set IDctrlNode 2; # node where displacement is read for displacement control
set IDctrlDOF 1; # degree of freedom of displacement read for displacement contro
set Dmax [expr 0.01*$LCol]; # maximum displacement of pushover. push to 10% drift.
set Dincr [expr 0.001*$LCol]; # displacement increment for pushover. you want this to be very small, but not too small to slow down the analysis
# create load pattern for lateral pushover load
set Hload $Weight; # define the lateral load as a proportion of the weight so that the pseudo time equals the lateral-load coefficient when using linear load pattern
pattern Plain 200 Linear {; # define load pattern -- generalized
load 2 $Hload 0.0 0.0 0.0 0.0 0.0; # define lateral load in static lateral analysis
}
# ----------- set up analysis parameters
# CONSTRAINTS handler -- Determines how the constraint equations are enforced in the analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/617.htm)
# Plain Constraints -- Removes constrained degrees of freedom from the system of equations (only for homogeneous equations)
# Lagrange Multipliers -- Uses the method of Lagrange multipliers to enforce constraints
# Penalty Method -- Uses penalty numbers to enforce constraints --good for static analysis with non-homogeneous eqns (rigidDiaphragm)
# Transformation Method -- Performs a condensation of constrained degrees of freedom
constraints Plain;
# DOF NUMBERER (number the degrees of freedom in the domain): (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/366.htm)
# determines the mapping between equation numbers and degrees-of-freedom
# Plain -- Uses the numbering provided by the user
# RCM -- Renumbers the DOF to minimize the matrix band-width using the Reverse Cuthill-McKee algorithm
numberer Plain
# SYSTEM (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/371.htm)
# Linear Equation Solvers (how to store and solve the system of equations in the analysis)
# -- provide the solution of the linear system of equations Ku = P. Each solver is tailored to a specific matrix topology.
# ProfileSPD -- Direct profile solver for symmetric positive definite matrices
# BandGeneral -- Direct solver for banded unsymmetric matrices
# BandSPD -- Direct solver for banded symmetric positive definite matrices
# SparseGeneral -- Direct solver for unsymmetric sparse matrices
# SparseSPD -- Direct solver for symmetric sparse matrices
# UmfPack -- Direct UmfPack solver for unsymmetric matrices
system BandGeneral
# TEST: # convergence test to
# Convergence TEST (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/360.htm)
# -- Accept the current state of the domain as being on the converged solution path
# -- determine if convergence has been achieved at the end of an iteration step
# NormUnbalance -- Specifies a tolerance on the norm of the unbalanced load at the current iteration
# NormDispIncr -- Specifies a tolerance on the norm of the displacement increments at the current iteration
# EnergyIncr-- Specifies a tolerance on the inner product of the unbalanced load and displacement increments at the current iteration
set Tol 1.e-8; # Convergence Test: tolerance
set maxNumIter 6; # Convergence Test: maximum number of iterations that will be performed before "failure to converge" is returned
set printFlag 0; # Convergence Test: flag used to print information on convergence (optional) # 1: print information on each step;
set TestType EnergyIncr ; # Convergence-test type
test $TestType $Tol $maxNumIter $printFlag;
# Solution ALGORITHM: -- Iterate from the last time step to the current (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/682.htm)
# Linear -- Uses the solution at the first iteration and continues
# Newton -- Uses the tangent at the current iteration to iterate to convergence
# ModifiedNewton -- Uses the tangent at the first iteration to iterate to convergence
set algorithmType Newton
algorithm $algorithmType;
# Static INTEGRATOR: -- determine the next time step for an analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/689.htm)
# LoadControl -- Specifies the incremental load factor to be applied to the loads in the domain
# DisplacementControl -- Specifies the incremental displacement at a specified DOF in the domain
# Minimum Unbalanced Displacement Norm -- Specifies the incremental load factor such that the residual displacement norm in minimized
# Arc Length -- Specifies the incremental arc-length of the load-displacement path
# Transient INTEGRATOR: -- determine the next time step for an analysis including inertial effects
# Newmark -- The two parameter time-stepping method developed by Newmark
# HHT -- The three parameter Hilbert-Hughes-Taylor time-stepping method
# Central Difference -- Approximates velocity and acceleration by centered finite differences of displacement
integrator DisplacementControl $IDctrlNode $IDctrlDOF $Dincr
# ANALYSIS -- defines what type of analysis is to be performed (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/324.htm)
# Static Analysis -- solves the KU=R problem, without the mass or damping matrices.
# Transient Analysis -- solves the time-dependent analysis. The time step in this type of analysis is constant. The time step in the output is also constant.
# variableTransient Analysis -- performs the same analysis type as the Transient Analysis object. The time step, however, is variable. This method is used when
# there are convergence problems with the Transient Analysis object at a peak or when the time step is too small. The time step in the output is also variable.
analysis Static
# --------------------------------- perform Static Pushover Analysis
set Nsteps [expr int($Dmax/$Dincr)]; # number of pushover analysis steps
set ok [analyze $Nsteps]; # this will return zero if no convergence problems were encountered
# ---------------------------------- in case of convergence problems
if {$ok != 0} {
# change some analysis parameters to achieve convergence
# performance is slower inside this loop
set ok 0;
set controlDisp 0.0; # start from zero
set D0 0.0; # start from zero
set Dstep [expr ($controlDisp-$D0)/($Dmax-$D0)]
while {$Dstep < 1.0 && $ok == 0} {
set controlDisp [nodeDisp $IDctrlNode $IDctrlDOF ]
set Dstep [expr ($controlDisp-$D0)/($Dmax-$D0)]
set ok [analyze 1 ]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 2000 0
algorithm Newton -initial
set ok [analyze 1 ]
test $TestType $Tol $maxNumIter 0
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 ]
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 ]
algorithm $algorithmType
}
}
}; # end if ok !0
puts "DonePushover"

View file

@ -0,0 +1,256 @@
# --------------------------------------------------------------------------------------------------
# Example 2. 2D cantilever column, dynamic eq ground motion
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
# units: kip, inch, sec
wipe; # clear memory of all past model definitions
file mkdir Data; # create data directory
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
# define GEOMETRY -------------------------------------------------------------
set LCol 432; # column length
set Weight 2000.; # superstructure weight
# define section geometry
set HCol 60; # Column Depth
set BCol 60; # Column Width
# calculated parameters
set PCol $Weight; # nodal dead-load weight per column
set g 386.4; # g.
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol*1000]; # cross-sectional area, make stiff
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS & SECTIONS -------------------------------------------------------------
set ColMatTagFlex 2; # assign a tag number to the column flexural behavior
set ColMatTagAxial 3; # assign a tag number to the column axial behavior
set ColSecTag 1; # assign a tag number to the column section tag
set BeamSecTag 2; # assign a tag number to the beam section tag
# MATERIAL parameters
set fc -4.; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*sqrt(-$fc*1000)]; # Concrete Elastic Modulus (the term in sqr root needs to be in psi
# COLUMN section
# calculated stiffness parameters
set EICol [expr $Ec*$IzCol]; # EI, for moment-curvature relationship
set EACol [expr $Ec*$ACol]; # EA, for axial-force-strain relationship
set MyCol 130000; # yield moment
set PhiYCol 0.65e-4; # yield curvature
set EIColCrack [expr $MyCol/$PhiYCol]; # cracked section inertia
set b 0.01 ; # strain-hardening ratio (ratio between post-yield tangent and initial elastic tangent)
uniaxialMaterial Steel01 $ColMatTagFlex $MyCol $EIColCrack $b; # bilinear behavior for flexure
uniaxialMaterial Elastic $ColMatTagAxial $EACol; # this is not used as a material, this is an axial-force-strain response
section Aggregator $ColSecTag $ColMatTagAxial P $ColMatTagFlex Mz; # combine axial and flexural behavior into one section (no P-M interaction here)
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
geomTransf Linear $ColTransfTag ;
# element connectivity:
set numIntgrPts 5; # number of integration points for force-based element
element nonlinearBeamColumn 1 1 2 $numIntgrPts $ColSecTag $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 2 globalForce; # element forces -- column
recorder Element -file Data/ForceColSec1.out -time -ele 1 section 1 force; # Column section forces, axial and moment, node i
recorder Element -file Data/DefoColSec1.out -time -ele 1 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file Data/ForceColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file Data/DefoColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# Gravity-analysis parameters -- load-controlled static analysis
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"
# DYNAMIC EQ ANALYSIS --------------------------------------------------------
# Uniform Earthquake ground motion (uniform acceleration input at all support nodes)
set GMdirection 1; # ground-motion direction
set GMfile "BM68elc.acc" ; # ground-motion filenames
set GMfact 1.; # ground-motion scaling factor
# set up ground-motion-analysis parameters
set DtAnalysis [expr 0.01]; # time-step Dt for lateral analysis
set TmaxAnalysis [expr 10.]; # maximum duration of ground-motion analysis -- should be 50*$sec
# DYNAMIC ANALYSIS PARAMETERS
# CONSTRAINTS handler -- Determines how the constraint equations are enforced in the analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/617.htm)
# Plain Constraints -- Removes constrained degrees of freedom from the system of equations
# Lagrange Multipliers -- Uses the method of Lagrange multipliers to enforce constraints
# Penalty Method -- Uses penalty numbers to enforce constraints
# Transformation Method -- Performs a condensation of constrained degrees of freedom
constraints Transformation ;
# DOF NUMBERER (number the degrees of freedom in the domain): (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/366.htm)
# determines the mapping between equation numbers and degrees-of-freedom
# Plain -- Uses the numbering provided by the user
# RCM -- Renumbers the DOF to minimize the matrix band-width using the Reverse Cuthill-McKee algorithm
numberer Plain
# SYSTEM (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/371.htm)
# Linear Equation Solvers (how to store and solve the system of equations in the analysis)
# -- provide the solution of the linear system of equations Ku = P. Each solver is tailored to a specific matrix topology.
# ProfileSPD -- Direct profile solver for symmetric positive definite matrices
# BandGeneral -- Direct solver for banded unsymmetric matrices
# BandSPD -- Direct solver for banded symmetric positive definite matrices
# SparseGeneral -- Direct solver for unsymmetric sparse matrices (-piv option)
# SparseSPD -- Direct solver for symmetric sparse matrices
# UmfPack -- Direct UmfPack solver for unsymmetric matrices
system SparseGeneral -piv
# TEST: # convergence test to
# Convergence TEST (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/360.htm)
# -- Accept the current state of the domain as being on the converged solution path
# -- determine if convergence has been achieved at the end of an iteration step
# NormUnbalance -- Specifies a tolerance on the norm of the unbalanced load at the current iteration
# NormDispIncr -- Specifies a tolerance on the norm of the displacement increments at the current iteration
# EnergyIncr-- Specifies a tolerance on the inner product of the unbalanced load and displacement increments at the current iteration
# RelativeNormUnbalance --
# RelativeNormDispIncr --
# RelativeEnergyIncr --
set Tol 1.e-8; # Convergence Test: tolerance
set maxNumIter 10; # Convergence Test: maximum number of iterations that will be performed before "failure to converge" is returned
set printFlag 0; # Convergence Test: flag used to print information on convergence (optional) # 1: print information on each step;
set TestType EnergyIncr; # Convergence-test type
test $TestType $Tol $maxNumIter $printFlag;
# Solution ALGORITHM: -- Iterate from the last time step to the current (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/682.htm)
# Linear -- Uses the solution at the first iteration and continues
# Newton -- Uses the tangent at the current iteration to iterate to convergence
# ModifiedNewton -- Uses the tangent at the first iteration to iterate to convergence
# NewtonLineSearch --
# KrylovNewton --
# BFGS --
# Broyden --
set algorithmType ModifiedNewton
algorithm $algorithmType;
# Static INTEGRATOR: -- determine the next time step for an analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/689.htm)
# LoadControl -- Specifies the incremental load factor to be applied to the loads in the domain
# DisplacementControl -- Specifies the incremental displacement at a specified DOF in the domain
# Minimum Unbalanced Displacement Norm -- Specifies the incremental load factor such that the residual displacement norm in minimized
# Arc Length -- Specifies the incremental arc-length of the load-displacement path
# Transient INTEGRATOR: -- determine the next time step for an analysis including inertial effects
# Newmark -- The two parameter time-stepping method developed by Newmark
# HHT -- The three parameter Hilbert-Hughes-Taylor time-stepping method
# Central Difference -- Approximates velocity and acceleration by centered finite differences of displacement
set NewmarkGamma 0.5; # Newmark-integrator gamma parameter (also HHT)
set NewmarkBeta 0.25; # Newmark-integrator beta parameter
integrator Newmark $NewmarkGamma $NewmarkBeta
# ANALYSIS -- defines what type of analysis is to be performed (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/324.htm)
# Static Analysis -- solves the KU=R problem, without the mass or damping matrices.
# Transient Analysis -- solves the time-dependent analysis. The time step in this type of analysis is constant. The time step in the output is also constant.
# variableTransient Analysis -- performs the same analysis type as the Transient Analysis object. The time step, however, is variable. This method is used when
# there are convergence problems with the Transient Analysis object at a peak or when the time step is too small. The time step in the output is also variable.
analysis Transient
# define DAMPING--------------------------------------------------------------------------------------
# apply Rayleigh DAMPING from $xDamp
# D=$alphaM*M + $betaKcurr*Kcurrent + $betaKcomm*KlastCommit + $beatKinit*$Kinitial
set xDamp 0.02; # 2% damping ratio
set lambda [eigen 1]; # eigenvalue mode 1
set omega [expr pow($lambda,0.5)];
set alphaM 0.; # M-prop. damping; D = alphaM*M
set betaKcurr 0.; # K-proportional damping; +beatKcurr*KCurrent
set betaKcomm [expr 2.*$xDamp/($omega)]; # K-prop. damping parameter; +betaKcomm*KlastCommitt
set betaKinit 0.; # initial-stiffness proportional damping +beatKinit*Kini
# define damping
rayleigh $alphaM $betaKcurr $betaKinit $betaKcomm; # RAYLEIGH damping
# --------------------------------- perform Dynamic Ground-Motion Analysis
# Uniform EXCITATION: acceleration input
set IDloadTag 400; # load tag
set dt 0.01; # time step for input ground motion
set GMfatt 1.0; # data in input file is in g Unifts -- ACCELERATION TH
set AccelSeries "Series -dt $dt -filePath $GMfile -factor $GMfatt"; # time series information
pattern UniformExcitation $IDloadTag $GMdirection -accel $AccelSeries ; # create Unifform excitation
set Nsteps [expr int($TmaxAnalysis/$DtAnalysis)];
set ok [analyze $Nsteps $DtAnalysis]; # actually perform analysis; returns ok=0 if analysis was successful
if {$ok != 0} { ; # if analysis was not successful.
# change some analysis parameters to achieve convergence
# performance is slower inside this loop
# Time-controlled analysis
set ok 0;
set controlTime [getTime];
while {$controlTime < $TmaxAnalysis && $ok == 0} {
set ok [analyze 1 $DtAnalysis]
set controlTime [getTime]
set ok [analyze 1 $DtAnalysis]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 1000 0
algorithm Newton -initial
set ok [analyze 1 $DtAnalysis]
test $TestType $Tol $maxNumIter 0
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmType
}
}
}; # end if ok !0
puts "Ground Motion Done. End Time: [getTime]"

View file

@ -0,0 +1,233 @@
# --------------------------------------------------------------------------------------------------
# Example 2. 2D cantilever column, static pushover
# element
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
# units: kip, inch, sec
wipe; # clear memory of all past model definitions
file mkdir Data; # create data directory
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
# define GEOMETRY -------------------------------------------------------------
set LCol 432; # column length
set Weight 2000.; # superstructure weight
# define section geometry
set HCol 60; # Column Depth
set BCol 60; # Column Width
# calculated parameters
set PCol $Weight; # nodal dead-load weight per column
set g 386.4; # g.
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol*1000]; # cross-sectional area, make stiff
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS & SECTIONS -------------------------------------------------------------
set ColMatTagFlex 2; # assign a tag number to the column flexural behavior
set ColMatTagAxial 3; # assign a tag number to the column axial behavior
set ColSecTag 1; # assign a tag number to the column section tag
set BeamSecTag 2; # assign a tag number to the beam section tag
# MATERIAL parameters
set fc -4.; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*sqrt(-$fc*1000)]; # Concrete Elastic Modulus (the term in sqr root needs to be in psi
# COLUMN section
# calculated stiffness parameters
set EICol [expr $Ec*$IzCol]; # EI, for moment-curvature relationship
set EACol [expr $Ec*$ACol]; # EA, for axial-force-strain relationship
set MyCol 130000; # yield moment
set PhiYCol 0.65e-4; # yield curvature
set EIColCrack [expr $MyCol/$PhiYCol]; # cracked section inertia
set b 0.01 ; # strain-hardening ratio (ratio between post-yield tangent and initial elastic tangent)
uniaxialMaterial Steel01 $ColMatTagFlex $MyCol $EIColCrack $b; # bilinear behavior for flexure
uniaxialMaterial Elastic $ColMatTagAxial $EACol; # this is not used as a material, this is an axial-force-strain response
section Aggregator $ColSecTag $ColMatTagAxial P $ColMatTagFlex Mz; # combine axial and flexural behavior into one section (no P-M interaction here)
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
geomTransf Linear $ColTransfTag ;
# element connectivity:
set numIntgrPts 5; # number of integration points for force-based element
element nonlinearBeamColumn 1 1 2 $numIntgrPts $ColSecTag $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file Data/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file Data/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file Data/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file Data/FCol.out -time -ele 2 globalForce; # element forces -- column
recorder Element -file Data/ForceColSec1.out -time -ele 1 section 1 force; # Column section forces, axial and moment, node i
recorder Element -file Data/DefoColSec1.out -time -ele 1 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file Data/ForceColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file Data/DefoColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# Gravity-analysis parameters -- load-controlled static analysis
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"
# STATIC PUSHOVER ANALYSIS --------------------------------------------------------------------------------------------------
#
# we need to set up parameters that are particular to the model.
set IDctrlNode 2; # node where displacement is read for displacement control
set IDctrlDOF 1; # degree of freedom of displacement read for displacement contro
set Dmax [expr 0.05*$LCol]; # maximum displacement of pushover. push to 10% drift.
set Dincr [expr 0.001*$LCol]; # displacement increment for pushover. you want this to be very small, but not too small to slow down the analysis
# create load pattern for lateral pushover load
set Hload $Weight; # define the lateral load as a proportion of the weight so that the pseudo time equals the lateral-load coefficient when using linear load pattern
pattern Plain 200 Linear {; # define load pattern -- generalized
load 2 $Hload 0.0 0.0 0.0 0.0 0.0; # define lateral load in static lateral analysis
}
# ----------- set up analysis parameters
# CONSTRAINTS handler -- Determines how the constraint equations are enforced in the analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/617.htm)
# Plain Constraints -- Removes constrained degrees of freedom from the system of equations (only for homogeneous equations)
# Lagrange Multipliers -- Uses the method of Lagrange multipliers to enforce constraints
# Penalty Method -- Uses penalty numbers to enforce constraints --good for static analysis with non-homogeneous eqns (rigidDiaphragm)
# Transformation Method -- Performs a condensation of constrained degrees of freedom
constraints Plain;
# DOF NUMBERER (number the degrees of freedom in the domain): (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/366.htm)
# determines the mapping between equation numbers and degrees-of-freedom
# Plain -- Uses the numbering provided by the user
# RCM -- Renumbers the DOF to minimize the matrix band-width using the Reverse Cuthill-McKee algorithm
numberer Plain
# SYSTEM (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/371.htm)
# Linear Equation Solvers (how to store and solve the system of equations in the analysis)
# -- provide the solution of the linear system of equations Ku = P. Each solver is tailored to a specific matrix topology.
# ProfileSPD -- Direct profile solver for symmetric positive definite matrices
# BandGeneral -- Direct solver for banded unsymmetric matrices
# BandSPD -- Direct solver for banded symmetric positive definite matrices
# SparseGeneral -- Direct solver for unsymmetric sparse matrices
# SparseSPD -- Direct solver for symmetric sparse matrices
# UmfPack -- Direct UmfPack solver for unsymmetric matrices
system BandGeneral
# TEST: # convergence test to
# Convergence TEST (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/360.htm)
# -- Accept the current state of the domain as being on the converged solution path
# -- determine if convergence has been achieved at the end of an iteration step
# NormUnbalance -- Specifies a tolerance on the norm of the unbalanced load at the current iteration
# NormDispIncr -- Specifies a tolerance on the norm of the displacement increments at the current iteration
# EnergyIncr-- Specifies a tolerance on the inner product of the unbalanced load and displacement increments at the current iteration
set Tol 1.e-8; # Convergence Test: tolerance
set maxNumIter 6; # Convergence Test: maximum number of iterations that will be performed before "failure to converge" is returned
set printFlag 0; # Convergence Test: flag used to print information on convergence (optional) # 1: print information on each step;
set TestType EnergyIncr ; # Convergence-test type
test $TestType $Tol $maxNumIter $printFlag;
# Solution ALGORITHM: -- Iterate from the last time step to the current (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/682.htm)
# Linear -- Uses the solution at the first iteration and continues
# Newton -- Uses the tangent at the current iteration to iterate to convergence
# ModifiedNewton -- Uses the tangent at the first iteration to iterate to convergence
set algorithmType Newton
algorithm $algorithmType;
# Static INTEGRATOR: -- determine the next time step for an analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/689.htm)
# LoadControl -- Specifies the incremental load factor to be applied to the loads in the domain
# DisplacementControl -- Specifies the incremental displacement at a specified DOF in the domain
# Minimum Unbalanced Displacement Norm -- Specifies the incremental load factor such that the residual displacement norm in minimized
# Arc Length -- Specifies the incremental arc-length of the load-displacement path
# Transient INTEGRATOR: -- determine the next time step for an analysis including inertial effects
# Newmark -- The two parameter time-stepping method developed by Newmark
# HHT -- The three parameter Hilbert-Hughes-Taylor time-stepping method
# Central Difference -- Approximates velocity and acceleration by centered finite differences of displacement
integrator DisplacementControl $IDctrlNode $IDctrlDOF $Dincr
# ANALYSIS -- defines what type of analysis is to be performed (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/324.htm)
# Static Analysis -- solves the KU=R problem, without the mass or damping matrices.
# Transient Analysis -- solves the time-dependent analysis. The time step in this type of analysis is constant. The time step in the output is also constant.
# variableTransient Analysis -- performs the same analysis type as the Transient Analysis object. The time step, however, is variable. This method is used when
# there are convergence problems with the Transient Analysis object at a peak or when the time step is too small. The time step in the output is also variable.
analysis Static
# --------------------------------- perform Static Pushover Analysis
set Nsteps [expr int($Dmax/$Dincr)]; # number of pushover analysis steps
set ok [analyze $Nsteps]; # this will return zero if no convergence problems were encountered
# ---------------------------------- in case of convergence problems
if {$ok != 0} {
# change some analysis parameters to achieve convergence
# performance is slower inside this loop
set ok 0;
set controlDisp 0.0; # start from zero
set D0 0.0; # start from zero
set Dstep [expr ($controlDisp-$D0)/($Dmax-$D0)]
while {$Dstep < 1.0 && $ok == 0} {
set controlDisp [nodeDisp $IDctrlNode $IDctrlDOF ]
set Dstep [expr ($controlDisp-$D0)/($Dmax-$D0)]
set ok [analyze 1 ]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 2000 0
algorithm Newton -initial
set ok [analyze 1 ]
test $TestType $Tol $maxNumIter 0
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 ]
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 ]
algorithm $algorithmType
}
}
}; # end if ok !0
puts "DonePushover"

View file

@ -0,0 +1,147 @@
# --------------------------------------------------------------------------------------------------
# Example 3. 2D Cantilever -- EQ ground motion
# Silvia Mazzoni & Frank McKenna, 2006
# execute this file after you have built the model, and after you apply gravity
#
# Uniform Earthquake ground motion (uniform acceleration input at all support nodes)
set GMdirection 1; # ground-motion direction
set GMfile "BM68elc.acc" ; # ground-motion filenames
set GMfact 1.5; # ground-motion scaling factor
# set up ground-motion-analysis parameters
set DtAnalysis [expr 0.01*$sec]; # time-step Dt for lateral analysis
set TmaxAnalysis [expr 10. *$sec]; # maximum duration of ground-motion analysis -- should be 50*$sec
# DYNAMIC ANALYSIS PARAMETERS
# CONSTRAINTS handler -- Determines how the constraint equations are enforced in the analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/617.htm)
# Plain Constraints -- Removes constrained degrees of freedom from the system of equations
# Lagrange Multipliers -- Uses the method of Lagrange multipliers to enforce constraints
# Penalty Method -- Uses penalty numbers to enforce constraints
# Transformation Method -- Performs a condensation of constrained degrees of freedom
constraints Transformation ;
# DOF NUMBERER (number the degrees of freedom in the domain): (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/366.htm)
# determines the mapping between equation numbers and degrees-of-freedom
# Plain -- Uses the numbering provided by the user
# RCM -- Renumbers the DOF to minimize the matrix band-width using the Reverse Cuthill-McKee algorithm
numberer Plain
# SYSTEM (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/371.htm)
# Linear Equation Solvers (how to store and solve the system of equations in the analysis)
# -- provide the solution of the linear system of equations Ku = P. Each solver is tailored to a specific matrix topology.
# ProfileSPD -- Direct profile solver for symmetric positive definite matrices
# BandGeneral -- Direct solver for banded unsymmetric matrices
# BandSPD -- Direct solver for banded symmetric positive definite matrices
# SparseGeneral -- Direct solver for unsymmetric sparse matrices (-piv option)
# SparseSPD -- Direct solver for symmetric sparse matrices
# UmfPack -- Direct UmfPack solver for unsymmetric matrices
system SparseGeneral -piv
# TEST: # convergence test to
# Convergence TEST (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/360.htm)
# -- Accept the current state of the domain as being on the converged solution path
# -- determine if convergence has been achieved at the end of an iteration step
# NormUnbalance -- Specifies a tolerance on the norm of the unbalanced load at the current iteration
# NormDispIncr -- Specifies a tolerance on the norm of the displacement increments at the current iteration
# EnergyIncr-- Specifies a tolerance on the inner product of the unbalanced load and displacement increments at the current iteration
# RelativeNormUnbalance --
# RelativeNormDispIncr --
# RelativeEnergyIncr --
set Tol 1.e-8; # Convergence Test: tolerance
set maxNumIter 10; # Convergence Test: maximum number of iterations that will be performed before "failure to converge" is returned
set printFlag 0; # Convergence Test: flag used to print information on convergence (optional) # 1: print information on each step;
set TestType EnergyIncr; # Convergence-test type
test $TestType $Tol $maxNumIter $printFlag;
# Solution ALGORITHM: -- Iterate from the last time step to the current (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/682.htm)
# Linear -- Uses the solution at the first iteration and continues
# Newton -- Uses the tangent at the current iteration to iterate to convergence
# ModifiedNewton -- Uses the tangent at the first iteration to iterate to convergence
# NewtonLineSearch --
# KrylovNewton --
# BFGS --
# Broyden --
set algorithmType ModifiedNewton
algorithm $algorithmType;
# Static INTEGRATOR: -- determine the next time step for an analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/689.htm)
# LoadControl -- Specifies the incremental load factor to be applied to the loads in the domain
# DisplacementControl -- Specifies the incremental displacement at a specified DOF in the domain
# Minimum Unbalanced Displacement Norm -- Specifies the incremental load factor such that the residual displacement norm in minimized
# Arc Length -- Specifies the incremental arc-length of the load-displacement path
# Transient INTEGRATOR: -- determine the next time step for an analysis including inertial effects
# Newmark -- The two parameter time-stepping method developed by Newmark
# HHT -- The three parameter Hilbert-Hughes-Taylor time-stepping method
# Central Difference -- Approximates velocity and acceleration by centered finite differences of displacement
set NewmarkGamma 0.5; # Newmark-integrator gamma parameter (also HHT)
set NewmarkBeta 0.25; # Newmark-integrator beta parameter
integrator Newmark $NewmarkGamma $NewmarkBeta
# ANALYSIS -- defines what type of analysis is to be performed (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/324.htm)
# Static Analysis -- solves the KU=R problem, without the mass or damping matrices.
# Transient Analysis -- solves the time-dependent analysis. The time step in this type of analysis is constant. The time step in the output is also constant.
# variableTransient Analysis -- performs the same analysis type as the Transient Analysis object. The time step, however, is variable. This method is used when
# there are convergence problems with the Transient Analysis object at a peak or when the time step is too small. The time step in the output is also variable.
analysis Transient
# define DAMPING--------------------------------------------------------------------------------------
# apply Rayleigh DAMPING from $xDamp
# D=$alphaM*M + $betaKcurr*Kcurrent + $betaKcomm*KlastCommit + $beatKinit*$Kinitial
set xDamp 0.02; # 2% damping ratio
set lambda [eigen 1]; # eigenvalue mode 1
set omega [expr pow($lambda,0.5)];
set alphaM 0.; # M-prop. damping; D = alphaM*M
set betaKcurr 0.; # K-proportional damping; +beatKcurr*KCurrent
set betaKcomm [expr 2.*$xDamp/($omega)]; # K-prop. damping parameter; +betaKcomm*KlastCommitt
set betaKinit 0.; # initial-stiffness proportional damping +beatKinit*Kini
# define damping
rayleigh $alphaM $betaKcurr $betaKinit $betaKcomm; # RAYLEIGH damping
# --------------------------------- perform Dynamic Ground-Motion Analysis
# Uniform EXCITATION: acceleration input
set IDloadTag 400; # load tag
set dt 0.01; # time step for input ground motion
set GMfatt 1.0; # data in input file is in g Unifts -- ACCELERATION TH
set AccelSeries "Series -dt $dt -filePath $GMfile -factor $GMfatt"; # time series information
pattern UniformExcitation $IDloadTag $GMdirection -accel $AccelSeries ; # create Unifform excitation
set Nsteps [expr int($TmaxAnalysis/$DtAnalysis)];
set ok [analyze $Nsteps $DtAnalysis]; # actually perform analysis; returns ok=0 if analysis was successful
if {$ok != 0} { ; # if analysis was not successful.
# change some analysis parameters to achieve convergence
# performance is slower inside this loop
# Time-controlled analysis
set ok 0;
set controlTime [getTime];
while {$controlTime < $TmaxAnalysis && $ok == 0} {
set ok [analyze 1 $DtAnalysis]
set controlTime [getTime]
set ok [analyze 1 $DtAnalysis]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 1000 0
algorithm Newton -initial
set ok [analyze 1 $DtAnalysis]
test $TestType $Tol $maxNumIter 0
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmType
}
}
}; # end if ok !0
puts "Ground Motion Done. End Time: [getTime]"

View file

@ -0,0 +1,119 @@
# --------------------------------------------------------------------------------------------------
# Example 3. 2D Cantilever -- Static Pushover
# Silvia Mazzoni & Frank McKenna, 2006
# execute this file after you have built the model, and after you apply gravity
#
# characteristics of pushover analysis
set Dmax [expr 0.05*$LCol]; # maximum displacement of pushover. push to 10% drift.
set Dincr [expr 0.001*$LCol]; # displacement increment for pushover. you want this to be very small, but not too small to slow down the analysis
# create load pattern for lateral pushover load
set Hload [expr $Weight]; # define the lateral load as a proportion of the weight so that the pseudo time equals the lateral-load coefficient when using linear load pattern
pattern Plain 200 Linear {; # define load pattern -- generalized
load 2 $Hload 0.0 0.0 0.0 0.0 0.0
}
# STATIC-ANALYSIS parameters
# CONSTRAINTS handler -- Determines how the constraint equations are enforced in the analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/617.htm)
# Plain Constraints -- Removes constrained degrees of freedom from the system of equations (only for homogeneous equations)
# Lagrange Multipliers -- Uses the method of Lagrange multipliers to enforce constraints
# Penalty Method -- Uses penalty numbers to enforce constraints --good for static analysis with non-homogeneous eqns (rigidDiaphragm)
# Transformation Method -- Performs a condensation of constrained degrees of freedom
set constraintsType Plain; # default;
constraints $constraintsType
# DOF NUMBERER (number the degrees of freedom in the domain): (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/366.htm)
# determines the mapping between equation numbers and degrees-of-freedom
# Plain -- Uses the numbering provided by the user
# RCM -- Renumbers the DOF to minimize the matrix band-width using the Reverse Cuthill-McKee algorithm
numberer Plain
# SYSTEM (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/371.htm)
# Linear Equation Solvers (how to store and solve the system of equations in the analysis)
# -- provide the solution of the linear system of equations Ku = P. Each solver is tailored to a specific matrix topology.
# ProfileSPD -- Direct profile solver for symmetric positive definite matrices
# BandGeneral -- Direct solver for banded unsymmetric matrices
# BandSPD -- Direct solver for banded symmetric positive definite matrices
# SparseGeneral -- Direct solver for unsymmetric sparse matrices
# SparseSPD -- Direct solver for symmetric sparse matrices
# UmfPack -- Direct UmfPack solver for unsymmetric matrices
system BandGeneral
# TEST: # convergence test to
# Convergence TEST (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/360.htm)
# -- Accept the current state of the domain as being on the converged solution path
# -- determine if convergence has been achieved at the end of an iteration step
# NormUnbalance -- Specifies a tolerance on the norm of the unbalanced load at the current iteration
# NormDispIncr -- Specifies a tolerance on the norm of the displacement increments at the current iteration
# EnergyIncr-- Specifies a tolerance on the inner product of the unbalanced load and displacement increments at the current iteration
set Tol 1.e-8; # Convergence Test: tolerance
set maxNumIter 6; # Convergence Test: maximum number of iterations that will be performed before "failure to converge" is returned
set printFlag 0; # Convergence Test: flag used to print information on convergence (optional) # 1: print information on each step;
set TestType EnergyIncr; # Convergence-test type
test $TestType $Tol $maxNumIter $printFlag;
# Solution ALGORITHM: -- Iterate from the last time step to the current (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/682.htm)
# Linear -- Uses the solution at the first iteration and continues
# Newton -- Uses the tangent at the current iteration to iterate to convergence
# ModifiedNewton -- Uses the tangent at the first iteration to iterate to convergence
set algorithmType Newton
algorithm $algorithmType;
# Static INTEGRATOR: -- determine the next time step for an analysis (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/689.htm)
# LoadControl -- Specifies the incremental load factor to be applied to the loads in the domain
# DisplacementControl -- Specifies the incremental displacement at a specified DOF in the domain
# Minimum Unbalanced Displacement Norm -- Specifies the incremental load factor such that the residual displacement norm in minimized
# Arc Length -- Specifies the incremental arc-length of the load-displacement path
# Transient INTEGRATOR: -- determine the next time step for an analysis including inertial effects
# Newmark -- The two parameter time-stepping method developed by Newmark
# HHT -- The three parameter Hilbert-Hughes-Taylor time-stepping method
# Central Difference -- Approximates velocity and acceleration by centered finite differences of displacement
integrator DisplacementControl $IDctrlNode $IDctrlDOF $Dincr
# ANALYSIS -- defines what type of analysis is to be performed (http://opensees.berkeley.edu/OpenSees/manuals/usermanual/324.htm)
# Static Analysis -- solves the KU=R problem, without the mass or damping matrices.
# Transient Analysis -- solves the time-dependent analysis. The time step in this type of analysis is constant. The time step in the output is also constant.
# variableTransient Analysis -- performs the same analysis type as the Transient Analysis object. The time step, however, is variable. This method is used when
# there are convergence problems with the Transient Analysis object at a peak or when the time step is too small. The time step in the output is also variable.
analysis Static
# --------------------------------- perform Static Pushover Analysis
set Nsteps [expr int($Dmax/$Dincr)]; # number of pushover analysis steps
set ok [analyze $Nsteps]; # this will return zero if no convergence problems were encountered
if {$ok != 0} {
# if analysis fails, we try some other stuff, performance is slower inside this loop
set ok 0;
set controlDisp 0.0;
set D0 0.0; # analysis starts from zero
set Dstep [expr ($controlDisp-$D0)/($Dmax-$D0)]
while {$Dstep < 1.0 && $ok == 0} {
set controlDisp [nodeDisp $IDctrlNode $IDctrlDOF ]
set Dstep [expr ($controlDisp-$D0)/($Dmax-$D0)]
set ok [analyze 1 ]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 2000 0
algorithm Newton -initial
set ok [analyze 1 ]
test $TestType $Tol $maxNumIter 0
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 ]
algorithm $algorithmType
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 ]
algorithm $algorithmType
}
}; # end while loop
}; # end if ok !0
puts "Pushover Done. Control Disp=[nodeDisp $IDctrlNode $IDctrlDOF]"

View file

@ -0,0 +1,116 @@
# --------------------------------------------------------------------------------------------------
# Example 3. 2D Cantilever -- Build Model
# elasticBeamColumn element
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear memory of all past model definitions
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
set dataDir Data; # set up name for data directory
file mkdir $dataDir/; # create data directory
set GMdir "../GMfiles"; # ground-motion file directory
# define UNITS ----------------------------------------------------------------------------
set in 1.; # define basic units -- output units
set kip 1.; # define basic units -- output units
set sec 1.; # define basic units -- output units
set LunitTXT "inch"; # define basic-unit text for output
set FunitTXT "kip"; # define basic-unit text for output
set TunitTXT "sec"; # define basic-unit text for output
set ft [expr 12.*$in]; # define engineering units
set ksi [expr $kip/pow($in,2)];
set psi [expr $ksi/1000.];
set lbf [expr $psi*$in*$in]; # pounds force
set pcf [expr $lbf/pow($ft,3)]; # pounds per cubic foot
set in2 [expr $in*$in]; # inch^2
set in4 [expr $in*$in*$in*$in]; # inch^4
set cm [expr $in/2.54]; # centimeter, needed for displacement input in MultipleSupport excitation
set PI [expr 2*asin(1.0)]; # define constants
set g [expr 32.2*$ft/pow($sec,2)]; # gravitational acceleration
set Ubig 1.e10; # a really large number
set Usmall [expr 1/$Ubig]; # a really small number
# define GEOMETRY -------------------------------------------------------------
set LCol [expr 36*$ft]; # column length
set Weight [expr 2000.*$kip]; # superstructure weight
# define section geometry
set HCol [expr 5.*$ft]; # Column Depth
set BCol [expr 5.*$ft]; # Column Width
# calculated parameters
set PCol [expr $Weight]; # nodal dead-load weight per column
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol]; # cross-sectional area
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# we need to set up parameters that are particular to the model.
set IDctrlNode 2; # node where displacement is read for displacement control
set IDctrlDOF 1; # degree of freedom of displacement read for displacement control
set iSupportNode "1"; # define support node, if needed.
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS -------------------------------------------------------------
# Material parameters
set fc [expr -4.*$ksi]; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*$ksi*sqrt(-$fc/$psi)]; # Concrete Elastic Modulus
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
set ColTransfType Linear ; # options, Linear PDelta Corotational
geomTransf $ColTransfType $ColTransfTag ;
# element connectivity:
element elasticBeamColumn 1 1 2 $ACol $Ec $IzCol $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file $dataDir/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file $dataDir/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file $dataDir/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file $dataDir/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file $dataDir/FCol.out -time -ele 1 globalForce; # element forces -- column
recorder Element -xml $dataDir/PlasticRotation.out -time -ele 1 plasticRotation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# ------------------------------------------------- apply gravity load
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"

View file

@ -0,0 +1,179 @@
# --------------------------------------------------------------------------------------------------
# Example 3. 2D Cantilever -- Build Model
# nonlinearBeamColumn element, uniaxial inelastic section
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear memory of all past model definitions
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
set dataDir Data; # set up name for data directory
file mkdir $dataDir/; # create data directory
set GMdir "../GMfiles"; # ground-motion file directory
# define UNITS ----------------------------------------------------------------------------
set in 1.; # define basic units -- output units
set kip 1.; # define basic units -- output units
set sec 1.; # define basic units -- output units
set LunitTXT "inch"; # define basic-unit text for output
set FunitTXT "kip"; # define basic-unit text for output
set TunitTXT "sec"; # define basic-unit text for output
set ft [expr 12.*$in]; # define engineering units
set ksi [expr $kip/pow($in,2)];
set psi [expr $ksi/1000.];
set lbf [expr $psi*$in*$in]; # pounds force
set pcf [expr $lbf/pow($ft,3)]; # pounds per cubic foot
set in2 [expr $in*$in]; # inch^2
set in4 [expr $in*$in*$in*$in]; # inch^4
set cm [expr $in/2.54]; # centimeter, needed for displacement input in MultipleSupport excitation
set PI [expr 2*asin(1.0)]; # define constants
set g [expr 32.2*$ft/pow($sec,2)]; # gravitational acceleration
set Ubig 1.e10; # a really large number
set Usmall [expr 1/$Ubig]; # a really small number
# define GEOMETRY -------------------------------------------------------------
set LCol [expr 36*$ft]; # column length
set Weight [expr 2000.*$kip]; # superstructure weight
# define section geometry
set HCol [expr 5.*$ft]; # Column Depth
set BCol [expr 5.*$ft]; # Column Width
# calculated parameters
set PCol [expr $Weight]; # nodal dead-load weight per column
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol]; # cross-sectional area
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# we need to set up parameters that are particular to the model.
set IDctrlNode 2; # node where displacement is read for displacement control
set IDctrlDOF 1; # degree of freedom of displacement read for displacement control
set iSupportNode "1"; # define support node, if needed.
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS & SECTIONS -------------------------------------------------------------
set ColSecTag 1; # assign a tag number to the column section
# define section geometry
set coverCol [expr 5.*$in]; # Column cover to reinforcing steel NA.
set numBarsCol 20; # number of longitudinal-reinforcement bars in column. (symmetric top & bot)
set barAreaCol [expr 2.25*$in2]; # area of longitudinal-reinforcement bars
# MATERIAL parameters -------------------------------------------------------------------
set IDconcU 1; # material ID tag -- unconfined cover concrete
set IDreinf 2; # material ID tag -- reinforcement
# nominal concrete compressive strength
set fc [expr -4.0*$ksi]; # CONCRETE Compressive Strength, ksi (+Tension, -Compression)
set Ec [expr 57*$ksi*sqrt(-$fc/$psi)]; # Concrete Elastic Modulus
# unconfined concrete
set fc1U $fc; # UNCONFINED concrete (todeschini parabolic model), maximum stress
set eps1U -0.003; # strain at maximum strength of unconfined concrete
set fc2U [expr 0.2*$fc1U]; # ultimate stress
set eps2U -0.01; # strain at ultimate stress
set lambda 0.1; # ratio between unloading slope at $eps2 and initial slope $Ec
# tensile-strength properties
set ftU [expr -0.14*$fc1U]; # tensile strength +tension
set Ets [expr $ftU/0.002]; # tension softening stiffness
# -----------
set Fy [expr 66.8*$ksi]; # STEEL yield stress
set Es [expr 29000.*$ksi]; # modulus of steel
set Bs 0.01; # strain-hardening ratio
set R0 18; # control the transition from elastic to plastic branches
set cR1 0.925; # control the transition from elastic to plastic branches
set cR2 0.15; # control the transition from elastic to plastic branches
uniaxialMaterial Concrete02 $IDconcU $fc1U $eps1U $fc2U $eps2U $lambda $ftU $Ets; # build cover concrete (unconfined)
uniaxialMaterial Steel02 $IDreinf $Fy $Es $Bs $R0 $cR1 $cR2; # build reinforcement material
# FIBER SECTION properties -------------------------------------------------------------
# symmetric section
# y
# ^
# |
# --------------------- -- --
# | o o o | | -- cover
# | | |
# | | |
# z <--- | + | H
# | | |
# | | |
# | o o o | | -- cover
# --------------------- -- --
# |-------- B --------|
#
# RC section:
set coverY [expr $HCol/2.0]; # The distance from the section z-axis to the edge of the cover concrete -- outer edge of cover concrete
set coverZ [expr $BCol/2.0]; # The distance from the section y-axis to the edge of the cover concrete -- outer edge of cover concrete
set coreY [expr $coverY-$coverCol]
set coreZ [expr $coverZ-$coverCol]
set nfY 16; # number of fibers for concrete in y-direction
set nfZ 4; # number of fibers for concrete in z-direction
section fiberSec $ColSecTag {; # Define the fiber section
patch quadr $IDconcU $nfZ $nfY -$coverY $coverZ -$coverY -$coverZ $coverY -$coverZ $coverY $coverZ; # Define the concrete patch
layer straight $IDreinf $numBarsCol $barAreaCol -$coreY $coreZ -$coreY -$coreZ; # top layer reinfocement
layer straight $IDreinf $numBarsCol $barAreaCol $coreY $coreZ $coreY -$coreZ; # bottom layer reinforcement
}; # end of fibersection definition
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
set ColTransfType Linear ; # options, Linear PDelta Corotational
geomTransf $ColTransfType $ColTransfTag ;
# element connectivity:
set numIntgrPts 5; # number of integration points for force-based element
element nonlinearBeamColumn 1 1 2 $numIntgrPts $ColSecTag $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file $dataDir/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file $dataDir/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file $dataDir/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file $dataDir/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file $dataDir/FCol.out -time -ele 2 globalForce; # element forces -- column
recorder Element -file $dataDir/ForceColSec1.out -time -ele 1 section 1 force; # Column section forces, axial and moment, node i
recorder Element -file $dataDir/DefoColSec1.out -time -ele 1 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file $dataDir/ForceColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file $dataDir/DefoColSec$numIntgrPts.out -time -ele 1 section 1 deformation; # section deformations, axial and curvature, node j
recorder Element -xml $dataDir/PlasticRotation.out -time -ele 1 plasticRotation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# Gravity-analysis parameters -- load-controlled static analysis
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"

View file

@ -0,0 +1,139 @@
# --------------------------------------------------------------------------------------------------
# Example 3. 2D Cantilever -- Build Model
# nonlinearBeamColumn element, inelastic fiber section
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 2 __
# | |
# | |
# | |
# (1) LCol
# | |
# | |
# | |
# =1= _|_ -------->X
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear memory of all past model definitions
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
set dataDir Data; # set up name for data directory
file mkdir $dataDir/; # create data directory
set GMdir "../GMfiles"; # ground-motion file directory
# define UNITS ----------------------------------------------------------------------------
set in 1.; # define basic units -- output units
set kip 1.; # define basic units -- output units
set sec 1.; # define basic units -- output units
set LunitTXT "inch"; # define basic-unit text for output
set FunitTXT "kip"; # define basic-unit text for output
set TunitTXT "sec"; # define basic-unit text for output
set ft [expr 12.*$in]; # define engineering units
set ksi [expr $kip/pow($in,2)];
set psi [expr $ksi/1000.];
set lbf [expr $psi*$in*$in]; # pounds force
set pcf [expr $lbf/pow($ft,3)]; # pounds per cubic foot
set in2 [expr $in*$in]; # inch^2
set in4 [expr $in*$in*$in*$in]; # inch^4
set cm [expr $in/2.54]; # centimeter, needed for displacement input in MultipleSupport excitation
set PI [expr 2*asin(1.0)]; # define constants
set g [expr 32.2*$ft/pow($sec,2)]; # gravitational acceleration
set Ubig 1.e10; # a really large number
set Usmall [expr 1/$Ubig]; # a really small number
# define GEOMETRY -------------------------------------------------------------
set LCol [expr 36*$ft]; # column length
set Weight [expr 2000.*$kip]; # superstructure weight
# define section geometry
set HCol [expr 5.*$ft]; # Column Depth
set BCol [expr 5.*$ft]; # Column Width
# calculated parameters
set PCol [expr $Weight]; # nodal dead-load weight per column
set Mass [expr $PCol/$g]; # nodal mass
# calculated geometry parameters
set ACol [expr $BCol*$HCol]; # cross-sectional area
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 0 $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 1; # node DX DY RZ
# we need to set up parameters that are particular to the model.
set IDctrlNode 2; # node where displacement is read for displacement control
set IDctrlDOF 1; # degree of freedom of displacement read for displacement control
set iSupportNode "1"; # define support node, if needed.
# nodal masses:
mass 2 $Mass 1e-9 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
# Define ELEMENTS & SECTIONS -------------------------------------------------------------
set ColMatTagFlex 2; # assign a tag number to the column flexural behavior
set ColMatTagAxial 3; # assign a tag number to the column axial behavior
set ColSecTag 1; # assign a tag number to the column section tag
set BeamSecTag 2; # assign a tag number to the beam section tag
# MATERIAL parameters
set fc [expr -4*$ksi]; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*$ksi*sqrt(-$fc/$psi)]; # Concrete Elastic Modulus
# COLUMN section
# calculated stiffness parameters
set EICol [expr $Ec*$IzCol]; # EI, for moment-curvature relationship
set EACol [expr $Ec*$ACol]; # EA, for axial-force-strain relationship
set MyCol [expr 130000*$kip*$in]; # yield moment
set PhiYCol [expr 0.65e-4/$in]; # yield curvature
set EIColCrack [expr $MyCol/$PhiYCol]; # cracked section inertia
set b 0.01 ; # strain-hardening ratio (ratio between post-yield tangent and initial elastic tangent)
uniaxialMaterial Steel01 $ColMatTagFlex $MyCol $EIColCrack $b; # bilinear behavior for flexure
uniaxialMaterial Elastic $ColMatTagAxial $EACol; # this is not used as a material, this is an axial-force-strain response
section Aggregator $ColSecTag $ColMatTagAxial P $ColMatTagFlex Mz; # combine axial and flexural behavior into one section (no P-M interaction here)
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
set ColTransfType Linear ; # options, Linear PDelta Corotational
geomTransf $ColTransfType $ColTransfTag ;
# element connectivity:
set numIntgrPts 5; # number of integration points for force-based element
element nonlinearBeamColumn 1 1 2 $numIntgrPts $ColSecTag $ColTransfTag; # self-explanatory when using variables
# Define RECORDERS -------------------------------------------------------------
recorder Node -file $dataDir/DFree.out -time -node 2 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file $dataDir/DBase.out -time -node 1 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file $dataDir/RBase.out -time -node 1 -dof 1 2 3 reaction; # support reaction
recorder Drift -file Data/Drift.out -time -iNode 1 -jNode 2 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file $dataDir/FCol.out -time -ele 2 globalForce; # element forces -- column
recorder Element -file $dataDir/ForceColSec1.out -time -ele 1 section 1 force; # Column section forces, axial and moment, node i
recorder Element -file $dataDir/DefoColSec1.out -time -ele 1 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file $dataDir/ForceColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file $dataDir/DefoColSec$numIntgrPts.out -time -ele 1 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
recorder Element -xml $dataDir/PlasticRotation.out -time -ele 1 plasticRotation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
pattern Plain 1 Linear {
load 2 0 -$PCol 0
}
# Gravity-analysis parameters -- load-controlled static analysis
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"

View file

@ -0,0 +1,78 @@
# --------------------------------------------------------------------------------------------------
# Example4. 2D Portal Frame-- Dynamic sine-wave input analysis
# Silvia Mazzoni, 2006
# execute this file after you have built the model, and after you apply gravity
#
# Uniform Sine-Wave ground motion (uniform acceleration input at all support nodes)
set GMdirection 1; # ground-motion direction
set GMSineAccAmpl [expr 0.5*$g]; # sine ground-motion acceleration amplitude (this is the support motion, not the free-node motion)
set TPeriodSine [expr 0.35*$sec]; # period of input sine wave
set DurationSine [expr 3.*$sec]; # duration of input sine wave
# set up ground-motion-analysis parameters
set DtAnalysis [expr 0.01*$sec]; # time-step Dt for lateral analysis
set TmaxAnalysis [expr 10. *$sec]; # maximum duration of ground-motion analysis -- should be 50*$sec
# ----------- set up analysis parameters
source LibAnalysisDynamicParameters.tcl; # constraintsHandler,DOFnumberer,system-ofequations,convergenceTest,solutionAlgorithm,integrator
# define DAMPING--------------------------------------------------------------------------------------
# apply Rayleigh DAMPING from $xDamp
# D=$alphaM*M + $betaKcurr*Kcurrent + $betaKcomm*KlastCommit + $beatKinit*$Kinitial
set xDamp 0.02; # 2% damping ratio
set lambda [eigen 1]; # eigenvalue mode 1
set omega [expr pow($lambda,0.5)];
set alphaM 0.; # M-prop. damping; D = alphaM*M
set betaKcurr 0.; # K-proportional damping; +beatKcurr*KCurrent
set betaKcomm [expr 2.*$xDamp/($omega)]; # K-prop. damping parameter; +betaKcomm*KlastCommitt
set betaKinit 0.; # initial-stiffness proportional damping +beatKinit*Kini
rayleigh $alphaM $betaKcurr $betaKinit $betaKcomm; # RAYLEIGH damping
# --------------------------------- perform Dynamic Ground-Motion Analysis
# the following commands are unique to the Sine-Wave excitation
set IDloadTag 400; # for uniformSupport excitation
set DtGround [expr 0.005*$sec]; # time-step Dt for input grond motion
set omegaSine [expr 2*$PI/$TPeriodSine];
set vel0 [expr $GMSineAccAmpl*(-1)/$omegaSine];
set AccelSeries "Sine 0. $DurationSine $TPeriodSine -factor $GMSineAccAmpl "
pattern UniformExcitation $IDloadTag $GMdirection -accel $AccelSeries -vel0 $vel0
set Nsteps [expr int($TmaxAnalysis/$DtAnalysis)];
set ok [analyze $Nsteps $DtAnalysis]; # actually perform analysis; returns ok=0 if analysis was successful
if {$ok != 0} { ; # analysis was not successful.
# --------------------------------------------------------------------------------------------------
# change some analysis parameters to achieve convergence
# performance is slower inside this loop
# Time-controlled analysis
set ok 0;
set controlTime [getTime];
while {$controlTime < $TmaxAnalysis && $ok == 0} {
set controlTime [getTime]
set ok [analyze 1 $DtAnalysis]
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 1000 0
algorithm Newton -initial
set ok [analyze 1 $DtAnalysis]
test $testTypeDynamic $TolDynamic $maxNumIterDynamic 0
algorithm $algorithmTypeDynamic
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmTypeDynamic
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch .8
set ok [analyze 1 $DtAnalysis]
algorithm $algorithmTypeDynamic
}
}
}; # end if ok !0
puts "Ground Motion Done. End Time: [getTime]"

View file

@ -0,0 +1,69 @@
# --------------------------------------------------------------------------------------------------
# Example4. 2D Portal Frame-- Static Pushover Analysis
# Silvia Mazzoni & Frank McKenna, 2006
# execute this file after you have built the model, and after you apply gravity
#
# we need to set up parameters that are particular to the model.
set IDctrlNode 3; # node where displacement is read for displacement control
set IDctrlDOF 1; # degree of freedom of displacement read for displacement contro
# characteristics of pushover analysis
set Dmax [expr 0.1*$LCol]; # maximum displacement of pushover. push to 10% drift.
set Dincr [expr 0.001*$LCol]; # displacement increment for pushover. you want this to be very small, but not too small to slow down the analysis
# create load pattern for lateral pushover load
set Hload [expr $Weight/2]; # define the lateral load as a proportion of the weight so that the pseudo time equals the lateral-load coefficient when using linear load pattern
set iPushNode "3 4"; # define nodes where lateral load is applied in static lateral analysis
pattern Plain 200 Linear {; # define load pattern -- generalized
foreach PushNode $iPushNode {
load $PushNode $Hload 0.0 0.0 0.0 0.0 0.0
}
}
# ----------- set up analysis parameters
source LibAnalysisStaticParameters.tcl; # constraintsHandler,DOFnumberer,system-ofequations,convergenceTest,solutionAlgorithm,integrator
# --------------------------------- perform Static Pushover Analysis
set Nsteps [expr int($Dmax/$Dincr)]; # number of pushover analysis steps
set ok [analyze $Nsteps]; # this will return zero if no convergence problems were encountered
set fmt1 "%s Pushover analysis: CtrlNode %.3i, dof %.1i, Disp=%.4f %s"; # format for screen/file output of DONE/PROBLEM analysis
if {$ok != 0} {
# if analysis fails, we try some other stuff, performance is slower inside this loop
set Dstep 0.0;
set ok 0
while {$Dstep <= 1.0 && $ok == 0} {
set controlDisp [nodeDisp $IDctrlNode $IDctrlDOF ]
set Dstep [expr $controlDisp/$Dmax]
set ok [analyze 1 ]
# if analysis fails, we try some other stuff
# performance is slower inside this loop global maxNumIterStatic; # max no. of iterations performed before "failure to converge" is ret'd
if {$ok != 0} {
puts "Trying Newton with Initial Tangent .."
test NormDispIncr $Tol 2000 0
algorithm Newton -initial
set ok [analyze 1]
test $testTypeStatic $TolStatic $maxNumIterStatic 0
algorithm $algorithmTypeStatic
}
if {$ok != 0} {
puts "Trying Broyden .."
algorithm Broyden 8
set ok [analyze 1 ]
algorithm $algorithmTypeStatic
}
if {$ok != 0} {
puts "Trying NewtonWithLineSearch .."
algorithm NewtonLineSearch 0.8
set ok [analyze 1]
algorithm $algorithmTypeStatic
}
}; # end while loop
}; # end if ok !0
# -----------------------------------------------------------------------------------------------------
if {$ok != 0 } {
puts [format $fmt1 "PROBLEM" $IDctrlNode $IDctrlDOF [nodeDisp $IDctrlNode $IDctrlDOF] $LunitTXT]
} else {
puts [format $fmt1 "DONE" $IDctrlNode $IDctrlDOF [nodeDisp $IDctrlNode $IDctrlDOF] $LunitTXT]
}

View file

@ -0,0 +1,108 @@
# --------------------------------------------------------------------------------------------------
# Example4. 2D Portal Frame-- Build Model
# elasticBeamColumn element
# Silvia Mazzoni & Frank McKenna, 2006
# ^Y
# |
# 3_________(3)________4 __
# | | |
# | | |
# | | |
# (1) (2) LCol
# | | |
# | | |
# | | |
# =1= =2= _|_ -------->X
# |----------LBeam------------|
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear memory of all past model definitions
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
set dataDir Data; # set up name of data directory
file mkdir $dataDir; # create data directory
set GMdir "GMfiles"; # ground-motion file directory
source LibUnits.tcl; # define basic and system units
# define GEOMETRY -------------------------------------------------------------
set LCol [expr 36*$ft]; # column length
set LBeam [expr 42*$ft]; # beam length
set Weight [expr 4000.*$kip]; # superstructure weight
# define section geometry
set HCol [expr 5.*$ft]; # Column Depth
set BCol [expr 4.*$ft]; # Column Width
set HBeam [expr 8.*$ft]; # Beam Depth
set BBeam [expr 5.*$ft]; # Beam Width
# calculated parameters
set PCol [expr $Weight/2]; # nodal dead-load weight per column
set Mass [expr $PCol/$g]; # nodal mass
set MCol [expr 1./12.*($Weight/$LBeam)*pow($LBeam,2)]; # beam-end moment due to distributed load.
# calculated geometry parameters
set ACol [expr $BCol*$HCol]; # cross-sectional area
set ABeam [expr $BBeam*$HBeam];
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
set IzBeam [expr 1./12.*$BBeam*pow($HBeam,3)]; # Beam moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 $LBeam 0
node 3 0 $LCol
node 4 $LBeam $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 0; # node DX DY RZ
fix 2 1 1 0; # node DX DY RZ
fix 3 0 0 0
fix 4 0 0 0
# nodal masses:
mass 3 $Mass 0. 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
mass 4 $Mass 0. 0.
# Define ELEMENTS -------------------------------------------------------------
# Material parameters
set fc [expr -4.*$ksi]; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*$ksi*sqrt(-$fc/$psi)]; # Concrete Elastic Modulus
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
set BeamTransfTag 2; # associate a tag to beam transformation (good practice to keep col and beam separate)
set ColTransfType Linear ; # options, Linear PDelta Corotational
geomTransf $ColTransfType $ColTransfTag ; # only columns can have PDelta effects (gravity effects)
geomTransf Linear $BeamTransfTag ;
# element connectivity:
element elasticBeamColumn 1 1 3 $ACol $Ec $IzCol $ColTransfTag; # self-explanatory when using variables
element elasticBeamColumn 2 2 4 $ACol $Ec $IzCol $ColTransfTag;
element elasticBeamColumn 3 3 4 $ABeam $Ec $IzBeam $BeamTransfTag;
# Define RECORDERS -------------------------------------------------------------
recorder Node -file $dataDir/DFree.out -time -node 3 4 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file $dataDir/DBase.out -time -node 1 2 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file $dataDir/RBase.out -time -node 1 2 -dof 1 2 3 reaction; # support reaction
recorder Drift -file $dataDir/Drift.out -time -iNode 1 2 -jNode 3 4 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file $dataDir/FCol.out -time -ele 1 2 globalForce; # element forces -- column
recorder Element -file $dataDir/FBeam.out -time -ele 3 globalForce; # element forces -- beam
# define GRAVITY -------------------------------------------------------------
set WzBeam [expr $Weight/$LBeam];
pattern Plain 1 Linear {
eleLoad -ele 3 -type -beamUniform -$WzBeam ; # distributed superstructure-weight on beam
}
# ------------------------------------------------- apply gravity load
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"

View file

@ -0,0 +1,178 @@
# --------------------------------------------------------------------------------------------------
# Example4. 2D Portal Frame-- Build Model
# nonlinearBeamColumn element, inelastic fiber section
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 3_________(3)________4 __
# | | |
# | | |
# | | |
# (1) (2) LCol
# | | |
# | | |
# | | |
# =1= =2= _|_ -------->X
# |----------LBeam------------|
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear memory of all past model definitions
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
set dataDir Data; # set up name of data directory
file mkdir $dataDir; # create data directory
set GMdir "GMfiles"; # ground-motion file directory
source LibUnits.tcl; # define basic and system units
# define GEOMETRY -------------------------------------------------------------
set LCol [expr 36*$ft]; # column length
set LBeam [expr 42*$ft]; # beam length
set Weight [expr 2000.*$kip]; # superstructure weight
# define section geometry
set HCol [expr 5.*$ft]; # Column Depth
set BCol [expr 5.*$ft]; # Column Width
set HBeam [expr 8.*$ft]; # Beam Depth
set BBeam [expr 5.*$ft]; # Beam Width
# calculated parameters
set PCol [expr $Weight/2]; # nodal dead-load weight per column
set Mass [expr $PCol/$g]; # nodal mass
set MCol [expr 1./12.*($Weight/$LBeam)*pow($LBeam,2)]; # beam-end moment due to distributed load.
# calculated geometry parameters
set ACol [expr $BCol*$HCol]; # cross-sectional area
set ABeam [expr $BBeam*$HBeam];
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
set IzBeam [expr 1./12.*$BBeam*pow($HBeam,3)]; # Beam moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 $LBeam 0
node 3 0 $LCol
node 4 $LBeam $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 0; # node DX DY RZ
fix 2 1 1 0; # node DX DY RZ
fix 3 0 0 0
fix 4 0 0 0
# nodal masses:
mass 3 $Mass 0. 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
mass 4 $Mass 0. 0.
# Define ELEMENTS & SECTIONS -------------------------------------------------------------
set ColSecTag 1; # assign a tag number to the column section
set BeamSecTag 2; # assign a tag number to the beam section
# define section geometry
set coverCol [expr 6.*$in]; # Column cover to reinforcing steel NA.
set numBarsCol 10; # number of longitudinal-reinforcement bars in each side of column section. (symmetric top & bot)
set barAreaCol [expr 2.25*$in2]; # area of longitudinal-reinforcement bars
# MATERIAL parameters -------------------------------------------------------------------
set IDconcU 1; # material ID tag -- unconfined cover concrete
set IDreinf 2; # material ID tag -- reinforcement
# nominal concrete compressive strength
set fc [expr -4.0*$ksi]; # CONCRETE Compressive Strength, ksi (+Tension, -Compression)
set Ec [expr 57*$ksi*sqrt(-$fc/$psi)]; # Concrete Elastic Modulus
# unconfined concrete
set fc1U $fc; # UNCONFINED concrete (todeschini parabolic model), maximum stress
set eps1U -0.003; # strain at maximum strength of unconfined concrete
set fc2U [expr 0.2*$fc1U]; # ultimate stress
set eps2U -0.05; # strain at ultimate stress
set lambda 0.1; # ratio between unloading slope at $eps2 and initial slope $Ec
# tensile-strength properties
set ftU [expr -0.14*$fc1U]; # tensile strength +tension
set Ets [expr $ftU/0.002]; # tension softening stiffness
# -----------
set Fy [expr 66.8*$ksi]; # STEEL yield stress
set Es [expr 29000.*$ksi]; # modulus of steel
set Bs 0.01; # strain-hardening ratio
set R0 18; # control the transition from elastic to plastic branches
set cR1 0.925; # control the transition from elastic to plastic branches
set cR2 0.15; # control the transition from elastic to plastic branches
uniaxialMaterial Concrete02 $IDconcU $fc1U $eps1U $fc2U $eps2U $lambda $ftU $Ets; # build cover concrete (unconfined)
uniaxialMaterial Steel02 $IDreinf $Fy $Es $Bs $R0 $cR1 $cR2; # build reinforcement material
# FIBER SECTION properties -------------------------------------------------------------
# symmetric section
# y
# ^
# |
# --------------------- -- --
# | o o o | | -- cover
# | | |
# | | |
# z <--- | + | H
# | | |
# | | |
# | o o o | | -- cover
# --------------------- -- --
# |-------- B --------|
#
# RC section:
set coverY [expr $HCol/2.0]; # The distance from the section z-axis to the edge of the cover concrete -- outer edge of cover concrete
set coverZ [expr $BCol/2.0]; # The distance from the section y-axis to the edge of the cover concrete -- outer edge of cover concrete
set coreY [expr $coverY-$coverCol]
set coreZ [expr $coverZ-$coverCol]
set nfY 16; # number of fibers for concrete in y-direction
set nfZ 4; # number of fibers for concrete in z-direction
section fiberSec $ColSecTag {; # Define the fiber section
patch quadr $IDconcU $nfZ $nfY -$coverY $coverZ -$coverY -$coverZ $coverY -$coverZ $coverY $coverZ; # Define the concrete patch
layer straight $IDreinf $numBarsCol $barAreaCol -$coreY $coreZ -$coreY -$coreZ; # top layer reinfocement
layer straight $IDreinf $numBarsCol $barAreaCol $coreY $coreZ $coreY -$coreZ; # bottom layer reinforcement
}; # end of fibersection definition
# BEAM section:
section Elastic $BeamSecTag $Ec $ABeam $IzBeam; # elastic beam section
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
set BeamTransfTag 2; # associate a tag to beam transformation (good practice to keep col and beam separate)
set ColTransfType Linear ; # options, Linear PDelta Corotational
geomTransf $ColTransfType $ColTransfTag ; # only columns can have PDelta effects (gravity effects)
geomTransf Linear $BeamTransfTag ;
# element connectivity:
set numIntgrPts 5; # number of integration points for force-based element
element nonlinearBeamColumn 1 1 3 $numIntgrPts $ColSecTag $ColTransfTag; # self-explanatory when using variables
element nonlinearBeamColumn 2 2 4 $numIntgrPts $ColSecTag $ColTransfTag;
element nonlinearBeamColumn 3 3 4 $numIntgrPts $BeamSecTag $BeamTransfTag;
# Define RECORDERS -------------------------------------------------------------
recorder Node -file $dataDir/DFree.out -time -node 3 4 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file $dataDir/DBase.out -time -node 1 2 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file $dataDir/RBase.out -time -node 1 2 -dof 1 2 3 reaction; # support reaction
recorder Drift -file $dataDir/Drift.out -time -iNode 1 2 -jNode 3 4 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file $dataDir/FCol.out -time -ele 1 2 globalForce; # element forces -- column
recorder Element -file $dataDir/FBeam.out -time -ele 3 globalForce; # element forces -- beam
recorder Element -file $dataDir/ForceColSec1.out -time -ele 1 2 section 1 force; # Column section forces, axial and moment, node i
recorder Element -file $dataDir/DefoColSec1.out -time -ele 1 2 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file $dataDir/ForceColSec$numIntgrPts.out -time -ele 1 2 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file $dataDir/DefoColSec$numIntgrPts.out -time -ele 1 2 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
recorder Element -file $dataDir/ForceBeamSec1.out -time -ele 3 section 1 force; # Beam section forces, axial and moment, node i
recorder Element -file $dataDir/DefoBeamSec1.out -time -ele 3 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file $dataDir/ForceBeamSec$numIntgrPts.out -time -ele 3 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file $dataDir/DefoBeamSec$numIntgrPts.out -time -ele 3 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
set WzBeam [expr $Weight/$LBeam];
pattern Plain 1 Linear {
eleLoad -ele 3 -type -beamUniform -$WzBeam ; # distributed superstructure-weight on beam
}
# Gravity-analysis parameters -- load-controlled static analysis
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"

View file

@ -0,0 +1,139 @@
# --------------------------------------------------------------------------------------------------
# Example4. 2D Portal Frame-- Build Model
# nonlinearBeamColumn element, uniaxial inelastic section
# Silvia Mazzoni & Frank McKenna, 2006
#
# ^Y
# |
# 3_________(3)________4 __
# | | |
# | | |
# | | |
# (1) (2) LCol
# | | |
# | | |
# | | |
# =1= =2= _|_ -------->X
# |----------LBeam------------|
#
# SET UP ----------------------------------------------------------------------------
wipe; # clear memory of all past model definitions
model BasicBuilder -ndm 2 -ndf 3; # Define the model builder, ndm=#dimension, ndf=#dofs
set dataDir Data; # set up name of data directory
file mkdir $dataDir; # create data directory
set GMdir "GMfiles"; # ground-motion file directory
source LibUnits.tcl; # define basic and system units
# define GEOMETRY -------------------------------------------------------------
set LCol [expr 36*$ft]; # column length
set LBeam [expr 42*$ft]; # beam length
set Weight [expr 4000.*$kip]; # superstructure weight
# define section geometry
set HCol [expr 5.*$ft]; # Column Depth
set BCol [expr 4.*$ft]; # Column Width
set HBeam [expr 8.*$ft]; # Beam Depth
set BBeam [expr 5.*$ft]; # Beam Width
# calculated parameters
set PCol [expr $Weight/2]; # nodal dead-load weight per column
set Mass [expr $PCol/$g]; # nodal mass
set MCol [expr 1./12.*($Weight/$LBeam)*pow($LBeam,2)]; # beam-end moment due to distributed load.
# calculated geometry parameters
set ACol [expr $BCol*$HCol]; # cross-sectional area
set ABeam [expr $BBeam*$HBeam];
set IzCol [expr 1./12.*$BCol*pow($HCol,3)]; # Column moment of inertia
set IzBeam [expr 1./12.*$BBeam*pow($HBeam,3)]; # Beam moment of inertia
# nodal coordinates:
node 1 0 0; # node#, X, Y
node 2 $LBeam 0
node 3 0 $LCol
node 4 $LBeam $LCol
# Single point constraints -- Boundary Conditions
fix 1 1 1 0; # node DX DY RZ
fix 2 1 1 0; # node DX DY RZ
fix 3 0 0 0
fix 4 0 0 0
# nodal masses:
mass 3 $Mass 0. 0.; # node#, Mx My Mz, Mass=Weight/g, neglect rotational inertia at nodes
mass 4 $Mass 0. 0.
# Define ELEMENTS & SECTIONS -------------------------------------------------------------
set ColMatTagFlex 2; # assign a tag number to the column flexural behavior
set ColMatTagAxial 3; # assign a tag number to the column axial behavior
set ColSecTag 1; # assign a tag number to the column section tag
set BeamSecTag 2; # assign a tag number to the beam section tag
# MATERIAL parameters
set fc [expr -4*$ksi]; # CONCRETE Compressive Strength (+Tension, -Compression)
set Ec [expr 57*$ksi*sqrt(-$fc/$psi)]; # Concrete Elastic Modulus
# COLUMN section
# calculated stiffness parameters
set EICol [expr $Ec*$IzCol]; # EI, for moment-curvature relationship
set EACol [expr $Ec*$ACol]; # EA, for axial-force-strain relationship
set MyCol [expr 130000*$kip*$in]; # yield moment
set PhiYCol [expr 0.65e-4/$in]; # yield curvature
set EIColCrack [expr $MyCol/$PhiYCol]; # cracked section inertia
set b 0.01 ; # strain-hardening ratio (ratio between post-yield tangent and initial elastic tangent)
uniaxialMaterial Steel01 $ColMatTagFlex $MyCol $EIColCrack $b; # bilinear behavior for flexure
uniaxialMaterial Elastic $ColMatTagAxial $EACol; # this is not used as a material, this is an axial-force-strain response
section Aggregator $ColSecTag $ColMatTagAxial P $ColMatTagFlex Mz; # combine axial and flexural behavior into one section (no P-M interaction here)
# BEAM section:
section Elastic $BeamSecTag $Ec $ABeam $IzBeam; # elastic beam section
# define geometric transformation: performs a linear geometric transformation of beam stiffness and resisting force from the basic system to the global-coordinate system
set ColTransfTag 1; # associate a tag to column transformation
set BeamTransfTag 2; # associate a tag to beam transformation (good practice to keep col and beam separate)
set ColTransfType Linear ; # options, Linear PDelta Corotational
geomTransf $ColTransfType $ColTransfTag ; # only columns can have PDelta effects (gravity effects)
geomTransf Linear $BeamTransfTag ;
# element connectivity:
set numIntgrPts 5; # number of integration points for force-based element
element nonlinearBeamColumn 1 1 3 $numIntgrPts $ColSecTag $ColTransfTag; # self-explanatory when using variables
element nonlinearBeamColumn 2 2 4 $numIntgrPts $ColSecTag $ColTransfTag;
element nonlinearBeamColumn 3 3 4 $numIntgrPts $BeamSecTag $BeamTransfTag;
# Define RECORDERS -------------------------------------------------------------
recorder Node -file $dataDir/DFree.out -time -node 3 4 -dof 1 2 3 disp; # displacements of free nodes
recorder Node -file $dataDir/DBase.out -time -node 1 2 -dof 1 2 3 disp; # displacements of support nodes
recorder Node -file $dataDir/RBase.out -time -node 1 2 -dof 1 2 3 reaction; # support reaction
recorder Drift -file $dataDir/Drift.out -time -iNode 1 2 -jNode 3 4 -dof 1 -perpDirn 2 ; # lateral drift
recorder Element -file $dataDir/FCol.out -time -ele 1 2 globalForce; # element forces -- column
recorder Element -file $dataDir/FBeam.out -time -ele 3 globalForce; # element forces -- beam
recorder Element -file $dataDir/ForceColSec1.out -time -ele 1 2 section 1 force; # Column section forces, axial and moment, node i
recorder Element -file $dataDir/DefoColSec1.out -time -ele 1 2 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file $dataDir/ForceColSec$numIntgrPts.out -time -ele 1 2 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file $dataDir/DefoColSec$numIntgrPts.out -time -ele 1 2 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
recorder Element -file $dataDir/ForceBeamSec1.out -time -ele 3 section 1 force; # Beam section forces, axial and moment, node i
recorder Element -file $dataDir/DefoBeamSec1.out -time -ele 3 section 1 deformation; # section deformations, axial and curvature, node i
recorder Element -file $dataDir/ForceBeamSec$numIntgrPts.out -time -ele 3 section $numIntgrPts force; # section forces, axial and moment, node j
recorder Element -file $dataDir/DefoBeamSec$numIntgrPts.out -time -ele 3 section $numIntgrPts deformation; # section deformations, axial and curvature, node j
# define GRAVITY -------------------------------------------------------------
set WzBeam [expr $Weight/$LBeam];
pattern Plain 1 Linear {
eleLoad -ele 3 -type -beamUniform -$WzBeam ; # distributed superstructure-weight on beam
}
# Gravity-analysis parameters -- load-controlled static analysis
set Tol 1.0e-8; # convergence tolerance for test
constraints Plain; # how it handles boundary conditions
numberer Plain; # renumber dof's to minimize band-width (optimization), if you want to
system BandGeneral; # how to store and solve the system of equations in the analysis
test NormDispIncr $Tol 6 ; # determine if convergence has been achieved at the end of an iteration step
algorithm Newton; # use Newton's solution algorithm: updates tangent stiffness at every iteration
set NstepGravity 10; # apply gravity in 10 steps
set DGravity [expr 1./$NstepGravity]; # first load increment;
integrator LoadControl $DGravity; # determine the next time step for an analysis
analysis Static; # define type of analysis static or transient
analyze $NstepGravity; # apply gravity
# ------------------------------------------------- maintain constant gravity loads and reset time to zero
loadConst -time 0.0
puts "Model Built"

View file

@ -0,0 +1,302 @@
{
"schema_version": 1,
"meta": {
"name": "Eigen - Two-Storey One-Bay Frame",
"description": "Two-storey one-bay elastic frame with two-mode eigenvalue analysis.",
"author": "OpenSees Wiki / Chopra Example 10.5",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "N1",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "N2",
"coords": [
240.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 3,
"name": "N3",
"coords": [
0.0,
120.0,
0.0
],
"mass": [
0.25906735751295334,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "N4",
"coords": [
240.0,
120.0,
0.0
],
"mass": [
0.25906735751295334,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 5,
"name": "N5",
"coords": [
0.0,
240.0,
0.0
],
"mass": [
0.12953367875647667,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 6,
"name": "N6",
"coords": [
240.0,
240.0,
0.0
],
"mass": [
0.12953367875647667,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [],
"sections": [
{
"id": 1,
"name": "Column-L1",
"type": "ElasticSection",
"E": 29000.0,
"A": 63.41,
"Iz": 640.0,
"Iy": null,
"G": null,
"J": null
},
{
"id": 2,
"name": "Frame-L2",
"type": "ElasticSection",
"E": 29000.0,
"A": 63.41,
"Iz": 320.0,
"Iy": null,
"G": null,
"J": null
}
],
"elements": [
{
"id": 1,
"name": "C1",
"type": "ElasticBeamColumn",
"nodes": [
1,
3
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 2,
"name": "C2",
"type": "ElasticBeamColumn",
"nodes": [
3,
5
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 3,
"name": "C3",
"type": "ElasticBeamColumn",
"nodes": [
2,
4
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 4,
"name": "C4",
"type": "ElasticBeamColumn",
"nodes": [
4,
6
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 5,
"name": "B1",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 6,
"name": "B2",
"type": "ElasticBeamColumn",
"nodes": [
5,
6
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
}
],
"mp_constraints": [],
"time_series": [],
"load_patterns": [],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Modal-2",
"type": "Modal",
"n_modes": 2,
"solver": "genBandArpack"
}
]
}

View file

@ -0,0 +1,106 @@
"""Eigen analysis of a two-storey one-bay frame.
OpenSees Wiki tutorial:
https://opensees.berkeley.edu/wiki/index.php?title=Eigen_analysis_of_a_two-storey_one-bay_frame
Two-storey one-bay elastic frame from Chopra Example 10.5:
- 2D frame, ndm=2 / ndf=3
- elastic columns + elastic beams
- lumped masses only in Ux
- modal analysis for the first two modes
Run from the repository root:
python examples/eigen_two_storey_one_bay_frame.py
Produces ``examples/eigen_two_storey_one_bay_frame.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
ElasticBeamColumn,
ElasticSection,
ModalCase,
Node,
Project,
ProjectMeta,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
M = 100.0 / 386.0
NUM_MODES = 2
A = 63.41
I = 320.0
E = 29000.0
L = 240.0
H = 120.0
REFERENCE_TCL = Path(__file__).resolve().parent / "data" / "EigenAnal_twoStoreyFrame1.tcl.txt"
def build_eigen_two_storey_one_bay_frame() -> Project:
return Project(
meta=ProjectMeta(
name="Eigen - Two-Storey One-Bay Frame",
author="OpenSees Wiki / Chopra Example 10.5",
description=(
"Two-storey one-bay elastic frame with two-mode eigenvalue analysis."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(id=1, name="N1", coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=2, name="N2", coords=(L, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=3, name="N3", coords=(0.0, H, 0.0), mass=(M, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=4, name="N4", coords=(L, H, 0.0), mass=(M, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=5, name="N5", coords=(0.0, 2.0 * H, 0.0), mass=(M / 2.0, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=6, name="N6", coords=(L, 2.0 * H, 0.0), mass=(M / 2.0, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
sections=[
ElasticSection(id=1, name="Column-L1", E=E, A=A, Iz=2.0 * I),
ElasticSection(id=2, name="Frame-L2", E=E, A=A, Iz=I),
],
elements=[
ElasticBeamColumn(id=1, name="C1", nodes=(1, 3), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=2, name="C2", nodes=(3, 5), section_id=2, geom_transf="Linear"),
ElasticBeamColumn(id=3, name="C3", nodes=(2, 4), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=4, name="C4", nodes=(4, 6), section_id=2, geom_transf="Linear"),
ElasticBeamColumn(id=5, name="B1", nodes=(3, 4), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=6, name="B2", nodes=(5, 6), section_id=2, geom_transf="Linear"),
],
analyses=[
ModalCase(id=1, name="Modal-2", n_modes=NUM_MODES),
],
)
def main() -> None:
project = build_eigen_two_storey_one_bay_frame()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Reference Tcl: {REFERENCE_TCL.name}")
print(f" Nodes: {len(project.nodes)}, elements: {len(project.elements)}")
print(f" Modes: {NUM_MODES}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,330 @@
{
"schema_version": 1,
"meta": {
"name": "Eigen - Two-Storey Shear Frame",
"description": "Two-storey shear frame with equalDOF floor constraints and two-mode eigenvalue analysis.",
"author": "OpenSees Wiki / Chopra Example 10.4",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "N1",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "N2",
"coords": [
288.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 3,
"name": "N3",
"coords": [
0.0,
144.0,
0.0
],
"mass": [
0.25906735751295334,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "N4",
"coords": [
288.0,
144.0,
0.0
],
"mass": [
0.25906735751295334,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 5,
"name": "N5",
"coords": [
0.0,
288.0,
0.0
],
"mass": [
0.12953367875647667,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 6,
"name": "N6",
"coords": [
288.0,
288.0,
0.0
],
"mass": [
0.12953367875647667,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [],
"sections": [
{
"id": 1,
"name": "Column-L1",
"type": "ElasticSection",
"E": 30000.0,
"A": 63.41,
"Iz": 640.0,
"Iy": null,
"G": null,
"J": null
},
{
"id": 2,
"name": "Column-L2",
"type": "ElasticSection",
"E": 30000.0,
"A": 63.41,
"Iz": 320.0,
"Iy": null,
"G": null,
"J": null
},
{
"id": 3,
"name": "Beam-Rigid",
"type": "ElasticSection",
"E": 30000.0,
"A": 63.41,
"Iz": 10000000000000.0,
"Iy": null,
"G": null,
"J": null
}
],
"elements": [
{
"id": 1,
"name": "C1",
"type": "ElasticBeamColumn",
"nodes": [
1,
3
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 2,
"name": "C2",
"type": "ElasticBeamColumn",
"nodes": [
3,
5
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 3,
"name": "C3",
"type": "ElasticBeamColumn",
"nodes": [
2,
4
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 4,
"name": "C4",
"type": "ElasticBeamColumn",
"nodes": [
4,
6
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 5,
"name": "B1",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 3,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 6,
"name": "B2",
"type": "ElasticBeamColumn",
"nodes": [
5,
6
],
"section_id": 3,
"geom_transf": "Linear",
"rho": 0.0
}
],
"mp_constraints": [
{
"retained_node": 3,
"constrained_node": 4,
"dofs": [
2,
3
]
},
{
"retained_node": 5,
"constrained_node": 6,
"dofs": [
2,
3
]
}
],
"time_series": [],
"load_patterns": [],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Modal-2",
"type": "Modal",
"n_modes": 2,
"solver": "genBandArpack"
}
]
}

View file

@ -0,0 +1,117 @@
"""Eigen analysis of a two-storey shear frame.
OpenSees Wiki tutorial:
https://opensees.berkeley.edu/wiki/index.php?title=Eigen_analysis_of_a_two-story_shear_frame
Idealized two-storey shear frame from Chopra Example 10.4:
- 2D frame, ndm=2 / ndf=3
- beams modeled as flexurally rigid (very large Iz)
- floor nodes tied with equalDOF in Uy and Rz
- lumped masses only in Ux
- modal analysis for the first two modes
Run from the repository root:
python examples/eigen_two_storey_shear_frame.py
Produces ``examples/eigen_two_storey_shear_frame.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
ElasticBeamColumn,
ElasticSection,
EqualDOFConstraint,
ModalCase,
Node,
Project,
ProjectMeta,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
M = 100.0 / 386.0
NUM_MODES = 2
AC = 63.41
IC = 320.0
E = 30000.0
IB = 10e12
AB = 63.41
L = 288.0
H = 144.0
REFERENCE_TCL = Path(__file__).resolve().parent / "data" / "EigenAnal_twoStoreyShearFrame7.tcl.txt"
def build_eigen_two_storey_shear_frame() -> Project:
return Project(
meta=ProjectMeta(
name="Eigen - Two-Storey Shear Frame",
author="OpenSees Wiki / Chopra Example 10.4",
description=(
"Two-storey shear frame with equalDOF floor constraints and "
"two-mode eigenvalue analysis."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(id=1, name="N1", coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=2, name="N2", coords=(L, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=3, name="N3", coords=(0.0, H, 0.0), mass=(M, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=4, name="N4", coords=(L, H, 0.0), mass=(M, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=5, name="N5", coords=(0.0, 2.0 * H, 0.0), mass=(M / 2.0, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=6, name="N6", coords=(L, 2.0 * H, 0.0), mass=(M / 2.0, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
mp_constraints=[
EqualDOFConstraint(retained_node=3, constrained_node=4, dofs=(2, 3)),
EqualDOFConstraint(retained_node=5, constrained_node=6, dofs=(2, 3)),
],
sections=[
ElasticSection(id=1, name="Column-L1", E=E, A=AC, Iz=2.0 * IC),
ElasticSection(id=2, name="Column-L2", E=E, A=AC, Iz=IC),
ElasticSection(id=3, name="Beam-Rigid", E=E, A=AB, Iz=IB),
],
elements=[
ElasticBeamColumn(id=1, name="C1", nodes=(1, 3), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=2, name="C2", nodes=(3, 5), section_id=2, geom_transf="Linear"),
ElasticBeamColumn(id=3, name="C3", nodes=(2, 4), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=4, name="C4", nodes=(4, 6), section_id=2, geom_transf="Linear"),
ElasticBeamColumn(id=5, name="B1", nodes=(3, 4), section_id=3, geom_transf="Linear"),
ElasticBeamColumn(id=6, name="B2", nodes=(5, 6), section_id=3, geom_transf="Linear"),
],
analyses=[
ModalCase(id=1, name="Modal-2", n_modes=NUM_MODES),
],
)
def main() -> None:
project = build_eigen_two_storey_shear_frame()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Reference Tcl: {REFERENCE_TCL.name}")
print(f" Nodes: {len(project.nodes)}, elements: {len(project.elements)}")
print(f" MP constraints: {len(project.mp_constraints)}, modes: {NUM_MODES}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,993 @@
{
"schema_version": 1,
"meta": {
"name": "Elastic Frame (OpenSees Ex 4)",
"description": "3-story 3-bay 2D elastic frame, AISC W-shape sections, gravity (distributed) + lateral (point) + 5-mode eigen.",
"author": "OpenSees Examples Manual",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [
{
"id": "X1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X2",
"ordinate": 360.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X3",
"ordinate": 720.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X4",
"ordinate": 1080.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"y_grid_lines": [
{
"id": "Y1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y2",
"ordinate": 162.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y3",
"ordinate": 324.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y4",
"ordinate": 486.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"z_grid_lines": [
{
"id": "Z1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "N1",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "N2",
"coords": [
360.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 3,
"name": "N3",
"coords": [
720.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 4,
"name": "N4",
"coords": [
1080.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 5,
"name": "N5",
"coords": [
0.0,
162.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 6,
"name": "N6",
"coords": [
360.0,
162.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 7,
"name": "N7",
"coords": [
720.0,
162.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 8,
"name": "N8",
"coords": [
1080.0,
162.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 9,
"name": "N9",
"coords": [
0.0,
324.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 10,
"name": "N10",
"coords": [
360.0,
324.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 11,
"name": "N11",
"coords": [
720.0,
324.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 12,
"name": "N12",
"coords": [
1080.0,
324.0,
0.0
],
"mass": [
0.766692546583851,
0.766692546583851,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 13,
"name": "N13",
"coords": [
0.0,
486.0,
0.0
],
"mass": [
0.6275879917184265,
0.6275879917184265,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 14,
"name": "N14",
"coords": [
360.0,
486.0,
0.0
],
"mass": [
0.6275879917184265,
0.6275879917184265,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 15,
"name": "N15",
"coords": [
720.0,
486.0,
0.0
],
"mass": [
0.6275879917184265,
0.6275879917184265,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 16,
"name": "N16",
"coords": [
1080.0,
486.0,
0.0
],
"mass": [
0.6275879917184265,
0.6275879917184265,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [],
"sections": [
{
"id": 1,
"name": "W14X257-ColExt",
"type": "ElasticSection",
"E": 29000.0,
"A": 75.6,
"Iz": 3400.0,
"Iy": 3400.0,
"G": 11200.0,
"J": 1.0
},
{
"id": 2,
"name": "W14X311-ColInt",
"type": "ElasticSection",
"E": 29000.0,
"A": 91.4,
"Iz": 4330.0,
"Iy": 4330.0,
"G": 11200.0,
"J": 1.0
},
{
"id": 3,
"name": "W33X118-Beam1",
"type": "ElasticSection",
"E": 29000.0,
"A": 34.7,
"Iz": 5900.0,
"Iy": 5900.0,
"G": 11200.0,
"J": 1.0
},
{
"id": 4,
"name": "W30X116-Beam2",
"type": "ElasticSection",
"E": 29000.0,
"A": 34.2,
"Iz": 4930.0,
"Iy": 4930.0,
"G": 11200.0,
"J": 1.0
},
{
"id": 5,
"name": "W24X68-Beam3",
"type": "ElasticSection",
"E": 29000.0,
"A": 20.1,
"Iz": 1830.0,
"Iy": 1830.0,
"G": 11200.0,
"J": 1.0
}
],
"elements": [
{
"id": 1,
"name": "Col-S1-C0",
"type": "ElasticBeamColumn",
"nodes": [
1,
5
],
"section_id": 1,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 2,
"name": "Col-S1-C1",
"type": "ElasticBeamColumn",
"nodes": [
2,
6
],
"section_id": 2,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 3,
"name": "Col-S1-C2",
"type": "ElasticBeamColumn",
"nodes": [
3,
7
],
"section_id": 2,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 4,
"name": "Col-S1-C3",
"type": "ElasticBeamColumn",
"nodes": [
4,
8
],
"section_id": 1,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 5,
"name": "Col-S2-C0",
"type": "ElasticBeamColumn",
"nodes": [
5,
9
],
"section_id": 1,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 6,
"name": "Col-S2-C1",
"type": "ElasticBeamColumn",
"nodes": [
6,
10
],
"section_id": 2,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 7,
"name": "Col-S2-C2",
"type": "ElasticBeamColumn",
"nodes": [
7,
11
],
"section_id": 2,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 8,
"name": "Col-S2-C3",
"type": "ElasticBeamColumn",
"nodes": [
8,
12
],
"section_id": 1,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 9,
"name": "Col-S3-C0",
"type": "ElasticBeamColumn",
"nodes": [
9,
13
],
"section_id": 1,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 10,
"name": "Col-S3-C1",
"type": "ElasticBeamColumn",
"nodes": [
10,
14
],
"section_id": 2,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 11,
"name": "Col-S3-C2",
"type": "ElasticBeamColumn",
"nodes": [
11,
15
],
"section_id": 2,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 12,
"name": "Col-S3-C3",
"type": "ElasticBeamColumn",
"nodes": [
12,
16
],
"section_id": 1,
"geom_transf": "PDelta",
"rho": 0.0
},
{
"id": 13,
"name": "Beam-F1-B0",
"type": "ElasticBeamColumn",
"nodes": [
5,
6
],
"section_id": 3,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 14,
"name": "Beam-F1-B1",
"type": "ElasticBeamColumn",
"nodes": [
6,
7
],
"section_id": 3,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 15,
"name": "Beam-F1-B2",
"type": "ElasticBeamColumn",
"nodes": [
7,
8
],
"section_id": 3,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 16,
"name": "Beam-F2-B0",
"type": "ElasticBeamColumn",
"nodes": [
9,
10
],
"section_id": 4,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 17,
"name": "Beam-F2-B1",
"type": "ElasticBeamColumn",
"nodes": [
10,
11
],
"section_id": 4,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 18,
"name": "Beam-F2-B2",
"type": "ElasticBeamColumn",
"nodes": [
11,
12
],
"section_id": 4,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 19,
"name": "Beam-F3-B0",
"type": "ElasticBeamColumn",
"nodes": [
13,
14
],
"section_id": 5,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 20,
"name": "Beam-F3-B1",
"type": "ElasticBeamColumn",
"nodes": [
14,
15
],
"section_id": 5,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 21,
"name": "Beam-F3-B2",
"type": "ElasticBeamColumn",
"nodes": [
15,
16
],
"section_id": 5,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Gravity",
"type": "Constant",
"factor": 1.0
},
{
"id": 2,
"name": "Lateral",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "Gravity",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [],
"element_loads": [
{
"element_id": 13,
"wy": -0.8229166666666666,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 14,
"wy": -0.8229166666666666,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 15,
"wy": -0.8229166666666666,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 16,
"wy": -0.8229166666666666,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 17,
"wy": -0.8229166666666666,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 18,
"wy": -0.8229166666666666,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 19,
"wy": -0.6736111111111112,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 20,
"wy": -0.6736111111111112,
"wz": 0.0,
"wx": 0.0
},
{
"element_id": 21,
"wy": -0.6736111111111112,
"wz": 0.0,
"wx": 0.0
}
]
},
{
"id": 2,
"name": "Lateral",
"type": "Plain",
"time_series_id": 2,
"nodal_loads": [
{
"node_id": 5,
"forces": [
220.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
},
{
"node_id": 9,
"forces": [
180.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
},
{
"node_id": 13,
"forces": [
90.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Gravity",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 1,
"load_factor_increment": 1.0,
"system": "BandGeneral",
"constraints": "Transformation",
"integrator": "LoadControl",
"algorithm": "Linear",
"test": "NormDispIncr",
"tolerance": 1e-10,
"max_iter": 10
},
{
"id": 2,
"name": "Gravity+Lateral",
"type": "Static",
"pattern_ids": [
1,
2
],
"n_steps": 1,
"load_factor_increment": 1.0,
"system": "BandGeneral",
"constraints": "Transformation",
"integrator": "LoadControl",
"algorithm": "Linear",
"test": "NormDispIncr",
"tolerance": 1e-10,
"max_iter": 10
},
{
"id": 3,
"name": "Modal-5",
"type": "Modal",
"n_modes": 5,
"solver": "genBandArpack"
}
]
}

313
examples/elastic_frame.py Normal file
View file

@ -0,0 +1,313 @@
"""Elastic Frame Example — OpenSees Examples Manual, Example 4.
3-story 3-bay 2D elastic moment-resisting frame under gravity
(distributed beam loads) + a lateral reference pattern (point loads
at each floor's leftmost joint) + a 5-mode eigenvalue analysis.
Matches the Tcl walkthrough at:
https://opensees.berkeley.edu/wiki/index.php?title=Elastic_Frame_Example
Model (kip-in-ksi, ndm=2, ndf=3):
Floor 3 (z = 486") 13 ──beam19── 14 ──beam20── 15 ──beam21── 16
│ │ │ │
col9 col10 col11 col12
│ │ │ │
Floor 2 (z = 324") 9 ──beam16── 10 ─beam17── 11 ──beam18── 12
│ │ │ │
col5 col6 col7 col8
│ │ │ │
Floor 1 (z = 162") 5 ──beam13── 6 ─beam14── 7 ──beam15── 8
│ │ │ │
col1 col2 col3 col4
│ │ │ │
Base (z = 0") 1 2 3 4
(fixed) (fixed) (fixed) (fixed)
x = 0 360 720 1080
Sections (AISC W-shapes):
- Exterior column (lines 1, 4): W14X257 A=75.6 Iz=3400
- Interior column (lines 2, 3): W14X311 A=91.4 Iz=4330
- Floor-1 beam: W33X118 A=34.7 Iz=5900
- Floor-2 beam: W30X116 A=34.2 Iz=4930
- Floor-3 beam: W24X68 A=20.1 Iz=1830
- E = 29000 ksi for all.
Columns use the PDelta geometric transformation to capture P-Δ; beams
use Linear. Gravity is a Constant time series with a uniform
distributed load per beam (reference tributary intensity Load / 4 /
bay — matches the Tcl ``eleLoad -type -beamUniform [expr -Load/(4*bay)]``).
The lateral pattern uses a Linear time series with single-node point
loads (220 / 180 / 90 kip at floors 1 / 2 / 3 respectively).
Expected results (verified against the Tcl reference):
- Gravity ΣFy at base: ≈ 2505 kip (Σw · Σbeam-length · 3 floors)
- Gravity+Lateral ΣFx: ≈ -490 kip
- First five periods (s): 1.0256, 0.3498, 0.1919, 0.1562, 0.1307
GUI walkthrough: File → Open → elastic_frame.osmodel → Analyze →
Run → "Gravity + Lateral" → Display → Show Force Diagram / Modal.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
ConstantTimeSeries,
CoordinateGridSystem,
ElasticBeamColumn,
ElasticSection,
GridSystem,
LinearTimeSeries,
ModalCase,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
StaticCase,
UniformElementLoad,
UnitSystem,
make_grid_lines,
)
from otko.services import load_project, save_project
# Frame geometry (inches).
BAY = 360.0 # 30 ft — bay width
H_STORY = 162.0 # 13.5 ft — story height
N_BAYS = 3
N_STORIES = 3
# Material + section (kip, in, ksi).
E = 29000.0
# Column sections — exterior W14X257 vs interior W14X311.
A_COL_EXT, IZ_COL_EXT = 75.6, 3400.0
A_COL_INT, IZ_COL_INT = 91.4, 4330.0
# Beam sections — per floor.
A_BEAM_F1, IZ_BEAM_F1 = 34.7, 5900.0 # W33X118 (floor 1)
A_BEAM_F2, IZ_BEAM_F2 = 34.2, 4930.0 # W30X116 (floor 2)
A_BEAM_F3, IZ_BEAM_F3 = 20.1, 1830.0 # W24X68 (floor 3)
# Gravity loading (total weight per floor, kip).
LOAD_F1 = 1185.0
LOAD_F2 = 1185.0
LOAD_F3 = 970.0
# Lateral loading (kip, applied at each floor's leftmost joint, +X).
P_F1 = 220.0
P_F2 = 180.0
P_F3 = 90.0
# Gravity constant.
G = 386.4 # in/s²
# ─── ID layout ─────────────────────────────────────────────────────
# Nodes: row-major, starting from (x=0, y=0). 4 columns × 4 rows = 16.
# row r (0=base, 1=floor-1, 2=floor-2, 3=floor-3), col c (0..3):
# id = 1 + r*4 + c
#
# Elements:
# cols 1..12 : columns (bottom-to-top, left-to-right within each story)
# cols 13..21 : beams (bottom-to-top, left-to-right within each floor)
#
def _node_id(row: int, col: int) -> int:
return 1 + row * (N_BAYS + 1) + col
def _col_id(story: int, col: int) -> int:
# Story 1..3, col 0..3.
return (story - 1) * (N_BAYS + 1) + col + 1
def _beam_id(floor: int, bay: int) -> int:
# Floor 1..3, bay 0..(N_BAYS-1).
n_cols_total = N_STORIES * (N_BAYS + 1) # 12
return n_cols_total + (floor - 1) * N_BAYS + bay + 1
def build_elastic_frame() -> Project:
nodes: list[Node] = []
m_floor = {
1: LOAD_F1 / ((N_BAYS + 1) * G), # mass per node at floor 1
2: LOAD_F2 / ((N_BAYS + 1) * G),
3: LOAD_F3 / ((N_BAYS + 1) * G),
}
for r in range(N_STORIES + 1):
for c in range(N_BAYS + 1):
nid = _node_id(r, c)
x = c * BAY
y = r * H_STORY
if r == 0:
# Base nodes: fix tx, ty, rz (the only active DOFs in ndm=2 ndf=3).
restraint = (True, True, False, False, False, True)
mass = (0.0,) * 6
else:
# Floor nodes: all 6 slots free. The runner's dof_idx
# picks only (tx, ty, rz) = (0, 1, 5) out of this tuple
# when emitting 2D. A stray True at index 5 would fix Rz
# at every floor node and make the frame act rigid-joint.
restraint = (False,) * 6
m = m_floor[r]
mass = (m, m, 0.0, 0.0, 0.0, 0.0)
nodes.append(Node(
id=nid, name=f"N{nid}",
coords=(x, y, 0.0),
restraint=restraint, mass=mass,
))
# Sections: exterior col, interior col, beam-F1, beam-F2, beam-F3.
sections = [
ElasticSection(id=1, name="W14X257-ColExt",
E=E, A=A_COL_EXT, Iz=IZ_COL_EXT,
Iy=IZ_COL_EXT, G=11200.0, J=1.0),
ElasticSection(id=2, name="W14X311-ColInt",
E=E, A=A_COL_INT, Iz=IZ_COL_INT,
Iy=IZ_COL_INT, G=11200.0, J=1.0),
ElasticSection(id=3, name="W33X118-Beam1",
E=E, A=A_BEAM_F1, Iz=IZ_BEAM_F1,
Iy=IZ_BEAM_F1, G=11200.0, J=1.0),
ElasticSection(id=4, name="W30X116-Beam2",
E=E, A=A_BEAM_F2, Iz=IZ_BEAM_F2,
Iy=IZ_BEAM_F2, G=11200.0, J=1.0),
ElasticSection(id=5, name="W24X68-Beam3",
E=E, A=A_BEAM_F3, Iz=IZ_BEAM_F3,
Iy=IZ_BEAM_F3, G=11200.0, J=1.0),
]
# Elements — 12 columns (PDelta) + 9 beams (Linear).
elements: list[ElasticBeamColumn] = []
for s in range(1, N_STORIES + 1):
for c in range(N_BAYS + 1):
sec_id = 1 if c in (0, N_BAYS) else 2 # exterior vs interior
elements.append(ElasticBeamColumn(
id=_col_id(s, c), name=f"Col-S{s}-C{c}",
nodes=(_node_id(s - 1, c), _node_id(s, c)),
section_id=sec_id, geom_transf="PDelta",
))
beam_sec = {1: 3, 2: 4, 3: 5}
for f in range(1, N_STORIES + 1):
for b in range(N_BAYS):
elements.append(ElasticBeamColumn(
id=_beam_id(f, b), name=f"Beam-F{f}-B{b}",
nodes=(_node_id(f, b), _node_id(f, b + 1)),
section_id=beam_sec[f], geom_transf="Linear",
))
# Gravity distributed load per beam: w = -Load / (4 × bay). The Tcl
# reference divides by 4 (number of column lines), not by the number
# of bays — so the distributed load represents a *reference* tributary
# intensity, not the total floor weight spread over all beams.
# Corresponding reference values: w1 = w2 = -0.8229 kip/in, w3 = -0.6736.
floor_total = {1: LOAD_F1, 2: LOAD_F2, 3: LOAD_F3}
gravity_element_loads = [
UniformElementLoad(
element_id=_beam_id(f, b),
wy=-floor_total[f] / ((N_BAYS + 1) * BAY),
)
for f in range(1, N_STORIES + 1)
for b in range(N_BAYS)
]
# Lateral point loads at each floor's leftmost joint (+X).
lateral_nodes = {
_node_id(1, 0): P_F1,
_node_id(2, 0): P_F2,
_node_id(3, 0): P_F3,
}
return Project(
meta=ProjectMeta(
name="Elastic Frame (OpenSees Ex 4)",
author="OpenSees Examples Manual",
description=(
"3-story 3-bay 2D elastic frame, AISC W-shape sections, "
"gravity (distributed) + lateral (point) + 5-mode eigen."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2, ndf=3,
coord_systems=[
CoordinateGridSystem(
name="Global",
grid=GridSystem(
x_grid_lines=make_grid_lines(
"X", [c * BAY for c in range(N_BAYS + 1)],
),
y_grid_lines=make_grid_lines(
"Y", [r * H_STORY for r in range(N_STORIES + 1)],
),
z_grid_lines=make_grid_lines("Z", [0.0]),
),
),
],
nodes=nodes,
sections=sections,
elements=elements,
time_series=[
ConstantTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
],
load_patterns=[
PlainLoadPattern(
id=1, name="Gravity",
time_series_id=1,
element_loads=gravity_element_loads,
),
PlainLoadPattern(
id=2, name="Lateral",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=nid, forces=(P, 0, 0, 0, 0, 0))
for nid, P in lateral_nodes.items()
],
),
],
analyses=[
# Gravity alone — ΣFy at base should equal +3340 kip.
StaticCase(
id=1, name="Gravity",
pattern_ids=[1],
n_steps=1, load_factor_increment=1.0,
system="BandGeneral", constraints="Transformation",
integrator="LoadControl", algorithm="Linear",
test="NormDispIncr", tolerance=1e-10, max_iter=10,
),
# Gravity + lateral — ΣFx at base should equal -490 kip.
StaticCase(
id=2, name="Gravity+Lateral",
pattern_ids=[1, 2],
n_steps=1, load_factor_increment=1.0,
system="BandGeneral", constraints="Transformation",
integrator="LoadControl", algorithm="Linear",
test="NormDispIncr", tolerance=1e-10, max_iter=10,
),
# Eigen analysis on the lumped-mass model — 5 modes.
ModalCase(id=3, name="Modal-5", n_modes=5),
],
)
def main() -> None:
project = build_elastic_frame()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" ndm={project.ndm}, ndf={project.ndf}, "
f"units={project.meta.units.value}")
print(f" {len(project.nodes)} nodes, {len(project.elements)} elements")
print(f" Total gravity load: {LOAD_F1 + LOAD_F2 + LOAD_F3:.0f} kip")
print(f" Total lateral load: {P_F1 + P_F2 + P_F3:.0f} kip")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

240
examples/ex1a_canti2d.py Normal file
View file

@ -0,0 +1,240 @@
"""OpenSees Example 1a. 2D Elastic Cantilever Column.
OpenSees Wiki:
https://opensees.berkeley.edu/wiki/index.php?title=OpenSees_Example_1a._2D_Elastic_Cantilever_Column
This model keeps the original Example 1a geometry and packages both
lateral-load variants into a single OTKO project:
- Gravity preload in 10 static LoadControl steps
- Displacement-controlled static pushover
- UniformExcitation ground-motion analysis using ``BM68elc.acc``
Run from the repository root:
python examples/ex1a_canti2d.py
Produces ``examples/ex1a_canti2d.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
StaticCase,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
COLUMN_HEIGHT = 432.0
TOP_WEIGHT = 2000.0
TOP_MASS_X = 5.18
TOP_MASS_Y = 1.0e-9
AREA = 3_600_000_000.0
E_MODULUS = 4227.0
IZ = 1_080_000.0
PUSH_STEP = 0.1
PUSH_TARGET = 100.0
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.02
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex1a.Canti2D.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex1a.Canti2D.EQ.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex1a_canti2d() -> Project:
values = _ground_motion_values()
return Project(
meta=ProjectMeta(
name="OpenSees Ex 1a - 2D Elastic Cantilever Column",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=(
"Original Ex 1a elastic cantilever column with shared gravity "
"preload, static pushover, and BM68elc base-excitation cases."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1,
name="Base",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(
id=2,
name="Top",
coords=(0.0, COLUMN_HEIGHT, 0.0),
mass=(TOP_MASS_X, TOP_MASS_Y, 0.0, 0.0, 0.0, 0.0),
),
],
sections=[
ElasticSection(
id=1,
name="Column-Elastic",
E=E_MODULUS,
A=AREA,
Iz=IZ,
Iy=IZ,
G=1.0,
J=1.0,
),
],
elements=[
ElasticBeamColumn(
id=1,
name="Column",
nodes=(1, 2),
section_id=1,
geom_transf="Linear",
),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
PathTimeSeries(
id=3,
name="BM68elc",
dt=GROUND_DT,
factor=GROUND_FACTOR,
values=values,
file_path=str(GROUND_MOTION_FILE.name),
),
],
load_patterns=[
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(
node_id=2,
forces=(0.0, -TOP_WEIGHT, 0.0, 0.0, 0.0, 0.0),
),
],
),
PlainLoadPattern(
id=2,
name="Pushover-X",
time_series_id=2,
nodal_loads=[
NodalLoad(
node_id=2,
forces=(TOP_WEIGHT, 0.0, 0.0, 0.0, 0.0, 0.0),
),
],
),
UniformExcitationPattern(
id=3,
name="GroundMotion-X",
direction=1,
accel_series_id=3,
),
],
analyses=[
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=10,
load_factor_increment=0.1,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
PushoverCase(
id=2,
name="Push",
preload_case_ids=[1],
pattern_ids=[2],
control_node=2,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
TransientCase(
id=3,
name="Earthquake",
preload_case_ids=[1],
pattern_ids=[3],
dt=ANALYSIS_DT,
n_steps=ANALYSIS_STEPS,
system="BandGeneral",
constraints="Plain",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
],
)
def main() -> None:
project = build_ex1a_canti2d()
project.validate_references()
gm = next(ts for ts in project.time_series if ts.id == 3)
print(f"Built '{project.meta.name}'")
print(f" Gravity + pushover + earthquake cases: {len(project.analyses)}")
print(f" Ground motion file: {GROUND_MOTION_FILE.name}")
print(f" Reference Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
print(f" Record points: {len(gm.values)}, dt = {GROUND_DT}s, factor = {GROUND_FACTOR}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

211
examples/ex1a_canti2d_eq.py Normal file
View file

@ -0,0 +1,211 @@
"""Time History Analysis of a 2D Elastic Cantilever Column.
OpenSees Wiki tutorial / examples-manual variant:
https://opensees.berkeley.edu/wiki/index.php?title=Time_History_Analysis_of_a_2D_Elastic_Cantilever_Column
This example mirrors ``Ex1a.Canti2D.EQ.modif.tcl`` but expresses it in
OTKO's declarative project model:
- 2D frame (ndm=2, ndf=3), kip-in-sec units
- One elastic cantilever column, fixed at the base
- Gravity preload applied in 10 static LoadControl steps
- Loma Prieta horizontal record ``A10000`` imported from a plain-text list
- UniformExcitation in DOF 1 (+X)
- Newmark average-acceleration transient with 2% mode-1 stiffness damping
Run from the repository root:
python examples/ex1a_canti2d_eq.py
Produces ``examples/ex1a_canti2d_eq.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import (
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
StaticCase,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project
from otko.services.peer_record import parse_plain_values
G = 386.0
COLUMN_HEIGHT = 432.0
TOP_WEIGHT = 2000.0
TOP_MASS_X = 5.18
AREA = 3600.0
E_MODULUS = 3225.0
IZ = 1_080_000.0
GROUND_DT = 0.005
ANALYSIS_DT = 0.01
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "A10000.txt"
REFERENCE_TCL = _ROOT / "data" / "Ex1a.Canti2D.EQ.modif.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex1a_canti2d_eq() -> Project:
values = _ground_motion_values()
n_steps = len(values) // 2
return Project(
meta=ProjectMeta(
name="2D Elastic Cantilever EQ (OpenSees Ex 1a)",
author="OpenSees Wiki / Examples Manual",
description=(
"Elastic 2D cantilever with gravity preload + horizontal "
"UniformExcitation time history from the A10000 record."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1,
name="Base",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(
id=2,
name="Top",
coords=(0.0, COLUMN_HEIGHT, 0.0),
mass=(TOP_MASS_X, 0.0, 0.0, 0.0, 0.0, 0.0),
),
],
sections=[
ElasticSection(
id=1,
name="RC-Elastic",
E=E_MODULUS,
A=AREA,
Iz=IZ,
Iy=IZ,
G=1.0,
J=1.0,
),
],
elements=[
ElasticBeamColumn(
id=1,
name="Column",
nodes=(1, 2),
section_id=1,
geom_transf="Linear",
),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
PathTimeSeries(
id=2,
name="A10000",
dt=GROUND_DT,
factor=G,
values=values,
file_path=str(GROUND_MOTION_FILE.name),
),
],
load_patterns=[
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(
node_id=2,
forces=(0.0, -TOP_WEIGHT, 0.0, 0.0, 0.0, 0.0),
),
],
),
UniformExcitationPattern(
id=2,
name="GroundMotion-X",
direction=1,
accel_series_id=2,
),
],
analyses=[
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=10,
load_factor_increment=0.1,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Linear",
test="NormDispIncr",
tolerance=1e-12,
max_iter=10,
),
TransientCase(
id=2,
name="Earthquake",
preload_case_ids=[1],
pattern_ids=[2],
dt=ANALYSIS_DT,
n_steps=n_steps,
system="BandGeneral",
constraints="Plain",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="Linear",
test="NormDispIncr",
tolerance=1e-12,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
],
)
def main() -> None:
project = build_ex1a_canti2d_eq()
project.validate_references()
gm = next(ts for ts in project.time_series if ts.id == 2)
print(f"Built '{project.meta.name}'")
print(f" Ground motion file: {GROUND_MOTION_FILE.name}")
print(f" Reference Tcl: {REFERENCE_TCL.name}")
print(f" Points: {len(gm.values)}, dt = {GROUND_DT}s, factor = g = {G}")
print(
f" Transient: {project.analyses[1].n_steps} steps at "
f"{ANALYSIS_DT}s (every second record point)"
)
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

227
examples/ex1b_portal2d.py Normal file
View file

@ -0,0 +1,227 @@
"""OpenSees Example 1b. Elastic Portal Frame.
OpenSees Wiki:
https://opensees.berkeley.edu/wiki/index.php?title=OpenSees_Example_1b._Elastic_Portal_Frame
This packages the original Example 1b portal frame into one OpenSees
Studio project with shared gravity preload and both lateral-load cases:
- static pushover
- base-excitation earthquake analysis with ``BM68elc.acc``
Run from the repository root:
python examples/ex1b_portal2d.py
Produces ``examples/ex1b_portal2d.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
StaticCase,
TransientCase,
UniformElementLoad,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
L_BEAM = 504.0
L_COL = 432.0
TOP_MASS = 5.18
A_COL = 3_600_000_000.0
IZ_COL = 1_080_000.0
A_BEAM = 5_760_000_000.0
IZ_BEAM = 4_423_680.0
E_MODULUS = 4227.0
GRAVITY_W = -7.94
LATERAL_NODE_LOAD = 2000.0
PUSH_STEP = 0.1
PUSH_TARGET = 10.0
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.02
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex1b.Portal2D.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex1b.Portal2D.EQ.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex1b_portal2d() -> Project:
values = _ground_motion_values()
return Project(
meta=ProjectMeta(
name="OpenSees Ex 1b - Elastic Portal Frame",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=(
"Original Ex 1b elastic portal frame with shared gravity "
"preload, static pushover, and BM68elc earthquake cases."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1,
name="Base-L",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(
id=2,
name="Base-R",
coords=(L_BEAM, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(id=3, name="Top-L", coords=(0.0, L_COL, 0.0), mass=(TOP_MASS, 0.0, 0.0, 0.0, 0.0, 0.0)),
Node(id=4, name="Top-R", coords=(L_BEAM, L_COL, 0.0), mass=(TOP_MASS, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
sections=[
ElasticSection(id=1, name="Column", E=E_MODULUS, A=A_COL, Iz=IZ_COL, Iy=IZ_COL, G=1.0, J=1.0),
ElasticSection(id=2, name="Beam", E=E_MODULUS, A=A_BEAM, Iz=IZ_BEAM, Iy=IZ_BEAM, G=1.0, J=1.0),
],
elements=[
ElasticBeamColumn(id=1, name="Col-L", nodes=(1, 3), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=2, name="Col-R", nodes=(2, 4), section_id=1, geom_transf="Linear"),
ElasticBeamColumn(id=3, name="Beam", nodes=(3, 4), section_id=2, geom_transf="Linear"),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
PathTimeSeries(
id=3,
name="BM68elc",
dt=GROUND_DT,
factor=GROUND_FACTOR,
values=values,
file_path=str(GROUND_MOTION_FILE.name),
),
],
load_patterns=[
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
element_loads=[UniformElementLoad(element_id=3, wy=GRAVITY_W)],
),
PlainLoadPattern(
id=2,
name="Pushover-X",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=3, forces=(LATERAL_NODE_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0)),
NodalLoad(node_id=4, forces=(LATERAL_NODE_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
),
UniformExcitationPattern(
id=3,
name="GroundMotion-X",
direction=1,
accel_series_id=3,
),
],
analyses=[
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=10,
load_factor_increment=0.1,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
PushoverCase(
id=2,
name="Push",
preload_case_ids=[1],
pattern_ids=[2],
control_node=3,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1, 2],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
TransientCase(
id=3,
name="Earthquake",
preload_case_ids=[1],
pattern_ids=[3],
dt=ANALYSIS_DT,
n_steps=ANALYSIS_STEPS,
system="BandGeneral",
constraints="Plain",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
],
)
def main() -> None:
project = build_ex1b_portal2d()
project.validate_references()
gm = next(ts for ts in project.time_series if ts.id == 3)
print(f"Built '{project.meta.name}'")
print(f" Gravity + pushover + earthquake cases: {len(project.analyses)}")
print(f" Ground motion file: {GROUND_MOTION_FILE.name}")
print(f" Reference Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
print(f" Record points: {len(gm.values)}, dt = {GROUND_DT}s, factor = {GROUND_FACTOR}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,250 @@
"""OpenSees Example 2a. Elastic Cantilever Column with variables.
OpenSees Wiki:
https://opensees.berkeley.edu/wiki/index.php?title=OpenSees_Example_2a._Elastic_Cantilever_Column_with_variables
This example mirrors the Tcl tutorial's teaching goal: the model is
defined from a small set of named parameters, then derived quantities
such as mass, section area, and stiffness are computed from them.
The resulting OTKO project contains:
- gravity preload
- static pushover
- earthquake base excitation with ``BM68elc.acc``
Run from the repository root:
python examples/ex2a_canti2d_elastic_element.py
Produces ``examples/ex2a_canti2d_elastic_element.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
StaticCase,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
# Geometry and loading variables from the Tcl tutorial.
L_COL = 432.0
WEIGHT = 2000.0
H_COL = 60.0
B_COL = 60.0
G_ACCEL = 386.4
FC = -4.0
# Derived properties.
P_COL = WEIGHT
MASS = P_COL / G_ACCEL
A_COL = B_COL * H_COL * 1000.0
IZ_COL = (1.0 / 12.0) * B_COL * H_COL**3
E_C = 57.0 * math.sqrt(-FC * 1000.0)
# Analysis variables.
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.01 * L_COL
PUSH_STEP = 0.001 * L_COL
H_LOAD = WEIGHT
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.01
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex2a.Canti2D.ElasticElement.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex2a.Canti2D.ElasticElement.EQ.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex2a_canti2d_elastic_element() -> Project:
values = _ground_motion_values()
return Project(
meta=ProjectMeta(
name="OpenSees Ex 2a - Elastic Cantilever Column with Variables",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=(
"Variable-driven elastic cantilever model with derived mass, "
"section, pushover, and earthquake analysis parameters."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1,
name="Base",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(
id=2,
name="Top",
coords=(0.0, L_COL, 0.0),
mass=(MASS, 1.0e-9, 0.0, 0.0, 0.0, 0.0),
),
],
sections=[
ElasticSection(
id=1,
name="Elastic-Column",
E=E_C,
A=A_COL,
Iz=IZ_COL,
Iy=IZ_COL,
G=1.0,
J=1.0,
),
],
elements=[
ElasticBeamColumn(
id=1,
name="Column",
nodes=(1, 2),
section_id=1,
geom_transf="Linear",
),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
PathTimeSeries(
id=3,
name="BM68elc",
dt=GROUND_DT,
factor=GROUND_FACTOR,
values=values,
file_path=str(GROUND_MOTION_FILE.name),
),
],
load_patterns=[
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=2, forces=(0.0, -P_COL, 0.0, 0.0, 0.0, 0.0)),
],
),
PlainLoadPattern(
id=200,
name="Pushover-X",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
),
UniformExcitationPattern(
id=400,
name="GroundMotion-X",
direction=1,
accel_series_id=3,
),
],
analyses=[
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=N_GRAVITY,
load_factor_increment=GRAVITY_STEP,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
PushoverCase(
id=2,
name="Push",
preload_case_ids=[1],
pattern_ids=[200],
control_node=2,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=6,
),
TransientCase(
id=3,
name="Earthquake",
preload_case_ids=[1],
pattern_ids=[400],
dt=ANALYSIS_DT,
n_steps=ANALYSIS_STEPS,
system="SparseGeneral",
constraints="Transformation",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="ModifiedNewton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
],
)
def main() -> None:
project = build_ex2a_canti2d_elastic_element()
project.validate_references()
gm = next(ts for ts in project.time_series if ts.id == 3)
print(f"Built '{project.meta.name}'")
print(f" LCol={L_COL}, Weight={WEIGHT}, ACol={A_COL:.1f}, Ec={E_C:.3f}")
print(f" Gravity + pushover + earthquake cases: {len(project.analyses)}")
print(f" Ground motion file: {GROUND_MOTION_FILE.name}")
print(f" Reference Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
print(f" Record points: {len(gm.values)}, dt = {GROUND_DT}s, factor = {GROUND_FACTOR}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,275 @@
"""OpenSees Example 2b. Nonlinear Cantilever Column: Uniaxial Inelastic Section.
OpenSees Wiki:
https://opensees.berkeley.edu/wiki/index.php?title=OpenSees_Example_2b._Nonlinear_Cantilever_Column:_Uniaxial_Inelastic_Section
This is the first nonlinear cantilever example in the OpenSees tutorial
sequence. The column uses:
- uniaxial elastic axial response
- uniaxial bilinear flexural response
- a SectionAggregator that combines P and Mz into one section
- a force-based nonlinear beam-column element
Run from the repository root:
python examples/ex2b_canti2d_inelastic_section.py
Produces ``examples/ex2b_canti2d_inelastic_section.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
AggregatorDOF,
ElasticUniaxial,
ForceBeamColumn,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
SectionAggregator,
StaticCase,
Steel01,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
# Geometry / mass variables.
L_COL = 432.0
WEIGHT = 2000.0
H_COL = 60.0
B_COL = 60.0
G_ACCEL = 386.4
P_COL = WEIGHT
MASS = P_COL / G_ACCEL
A_COL = B_COL * H_COL * 1000.0
IZ_COL = (1.0 / 12.0) * B_COL * H_COL**3
# Material / section variables.
FC = -4.0
E_C = 57.0 * math.sqrt(-FC * 1000.0)
EI_COL = E_C * IZ_COL
EA_COL = E_C * A_COL
MY_COL = 130000.0
PHI_Y_COL = 0.65e-4
EI_COL_CRACK = MY_COL / PHI_Y_COL
HARDENING_RATIO = 0.01
NUM_INT_PTS = 5
# Analysis variables.
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.05 * L_COL
PUSH_STEP = 0.001 * L_COL
H_LOAD = WEIGHT
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.01
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex2b.Canti2D.InelasticSection.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex2b.Canti2D.InelasticSection.EQ.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex2b_canti2d_inelastic_section() -> Project:
values = _ground_motion_values()
return Project(
meta=ProjectMeta(
name="OpenSees Ex 2b - Nonlinear Cantilever Column",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=(
"Nonlinear cantilever with an aggregated uniaxial section: "
"elastic axial P plus bilinear inelastic Mz in one force-based element."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1,
name="Base",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(
id=2,
name="Top",
coords=(0.0, L_COL, 0.0),
mass=(MASS, 1.0e-9, 0.0, 0.0, 0.0, 0.0),
),
],
materials=[
Steel01(
id=2,
name="Col-Flex",
Fy=MY_COL,
E0=EI_COL_CRACK,
b=HARDENING_RATIO,
),
ElasticUniaxial(
id=3,
name="Col-Axial",
E=EA_COL,
),
],
sections=[
SectionAggregator(
id=1,
name="Col-Section",
pairings=[
AggregatorDOF(material_id=3, dof="P"),
AggregatorDOF(material_id=2, dof="Mz"),
],
),
],
elements=[
ForceBeamColumn(
id=1,
name="Column",
nodes=(1, 2),
section_id=1,
integration_points=NUM_INT_PTS,
geom_transf="Linear",
),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=200, name="Lateral"),
PathTimeSeries(
id=400,
name="BM68elc",
dt=GROUND_DT,
factor=GROUND_FACTOR,
values=values,
file_path=str(GROUND_MOTION_FILE.name),
),
],
load_patterns=[
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=2, forces=(0.0, -P_COL, 0.0, 0.0, 0.0, 0.0)),
],
),
PlainLoadPattern(
id=200,
name="Pushover-X",
time_series_id=200,
nodal_loads=[
NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
),
UniformExcitationPattern(
id=400,
name="GroundMotion-X",
direction=1,
accel_series_id=400,
),
],
analyses=[
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=N_GRAVITY,
load_factor_increment=GRAVITY_STEP,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
PushoverCase(
id=2,
name="Push",
preload_case_ids=[1],
pattern_ids=[200],
control_node=2,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=6,
),
TransientCase(
id=3,
name="Earthquake",
preload_case_ids=[1],
pattern_ids=[400],
dt=ANALYSIS_DT,
n_steps=ANALYSIS_STEPS,
system="SparseGeneral",
constraints="Transformation",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="ModifiedNewton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
],
)
def main() -> None:
project = build_ex2b_canti2d_inelastic_section()
project.validate_references()
gm = next(ts for ts in project.time_series if ts.id == 400)
print(f"Built '{project.meta.name}'")
print(
f" LCol={L_COL}, EA={EA_COL:.1f}, My={MY_COL:.1f}, "
f"EIcr={EI_COL_CRACK:.1f}, b={HARDENING_RATIO}"
)
print(f" Gravity + pushover + earthquake cases: {len(project.analyses)}")
print(f" Ground motion file: {GROUND_MOTION_FILE.name}")
print(f" Reference Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
print(f" Record points: {len(gm.values)}, dt = {GROUND_DT}s, factor = {GROUND_FACTOR}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
"""OpenSees Example 2c. Nonlinear Cantilever Column: Inelastic Fiber Section.
OpenSees Wiki:
https://opensees.berkeley.edu/wiki/index.php?title=OpenSees_Example_2c._Nonlinear_Cantilever_Column:_Inelastic_Uniaxial_Materials_in_Fiber_Section
This example replaces the aggregated uniaxial section of Ex2b with a
fiber section built from inelastic uniaxial materials. The fiber section
couples axial and flexural behavior naturally through the section
integration.
Run from the repository root:
python examples/ex2c_canti2d_inelastic_fiber_section.py
Produces ``examples/ex2c_canti2d_inelastic_fiber_section.osmodel``.
"""
from __future__ import annotations
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
Concrete02,
FiberSection,
ForceBeamColumn,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
RectangularPatch,
Steel02,
StraightLayer,
TransientCase,
StaticCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
L_COL = 432.0
WEIGHT = 2000.0
H_COL = 60.0
B_COL = 60.0
G_ACCEL = 386.4
P_COL = WEIGHT
MASS = P_COL / G_ACCEL
A_COL = B_COL * H_COL
IZ_COL = (1.0 / 12.0) * B_COL * H_COL**3
COVER_COL = 5.0
NUM_BARS_COL = 5
BAR_AREA_COL = 2.25
FC = -4.0
EC = 57.0 * math.sqrt(-FC * 1000.0)
FC1U = FC
EPS1U = -0.003
FC2U = 0.2 * FC1U
EPS2U = -0.01
LAMBDA = 0.1
FTU = -0.14 * FC1U
ETS = FTU / 0.002
FY = 66.8
ES = 29000.0
BS = 0.01
R0 = 18.0
CR1 = 0.925
CR2 = 0.15
NUM_INT_PTS = 5
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.01 * L_COL
PUSH_STEP = 0.001 * L_COL
H_LOAD = WEIGHT
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.01
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex2c.Canti2D.InelasticFiberSection.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex2c.Canti2D.InelasticFiberSection.EQ.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex2c_canti2d_inelastic_fiber_section() -> Project:
values = _ground_motion_values()
cover_y = H_COL / 2.0
cover_z = B_COL / 2.0
core_y = cover_y - COVER_COL
core_z = cover_z - COVER_COL
return Project(
meta=ProjectMeta(
name="OpenSees Ex 2c - Inelastic Fiber-Section Cantilever",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=(
"Nonlinear cantilever with a fiber section built from "
"Concrete02 and Steel02 uniaxial materials."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(
id=1,
name="Base",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True),
),
Node(
id=2,
name="Top",
coords=(0.0, L_COL, 0.0),
mass=(MASS, 1.0e-9, 0.0, 0.0, 0.0, 0.0),
),
],
materials=[
Concrete02(
id=1,
name="Cover-Concrete",
fpc=FC1U,
epsc0=EPS1U,
fpcu=FC2U,
epsU=EPS2U,
lambda_=LAMBDA,
ft=FTU,
Ets=ETS,
),
Steel02(
id=2,
name="Rebar-Steel",
Fy=FY,
E0=ES,
b=BS,
R0=R0,
cR1=CR1,
cR2=CR2,
),
],
sections=[
FiberSection(
id=1,
name="RC-Fiber-Section",
patches=[
RectangularPatch(
material_id=1,
n_fib_y=16,
n_fib_z=4,
y_i=-cover_y,
z_i=-cover_z,
y_j=cover_y,
z_j=cover_z,
),
],
layers=[
StraightLayer(
material_id=2,
n_bars=NUM_BARS_COL,
bar_area=BAR_AREA_COL,
y_start=-core_y,
z_start=core_z,
y_end=-core_y,
z_end=-core_z,
),
StraightLayer(
material_id=2,
n_bars=NUM_BARS_COL,
bar_area=BAR_AREA_COL,
y_start=core_y,
z_start=core_z,
y_end=core_y,
z_end=-core_z,
),
],
),
],
elements=[
ForceBeamColumn(
id=1,
name="Column",
nodes=(1, 2),
section_id=1,
integration_points=NUM_INT_PTS,
geom_transf="Linear",
),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=200, name="Lateral"),
PathTimeSeries(
id=400,
name="BM68elc",
dt=GROUND_DT,
factor=GROUND_FACTOR,
values=values,
file_path=str(GROUND_MOTION_FILE.name),
),
],
load_patterns=[
PlainLoadPattern(
id=1,
name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=2, forces=(0.0, -P_COL, 0.0, 0.0, 0.0, 0.0)),
],
),
PlainLoadPattern(
id=200,
name="Pushover-X",
time_series_id=200,
nodal_loads=[
NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0)),
],
),
UniformExcitationPattern(
id=400,
name="GroundMotion-X",
direction=1,
accel_series_id=400,
),
],
analyses=[
StaticCase(
id=1,
name="Gravity",
pattern_ids=[1],
n_steps=N_GRAVITY,
load_factor_increment=GRAVITY_STEP,
system="BandGeneral",
constraints="Plain",
integrator="LoadControl",
algorithm="Newton",
test="NormDispIncr",
tolerance=1e-8,
max_iter=6,
),
PushoverCase(
id=2,
name="Push",
preload_case_ids=[1],
pattern_ids=[200],
control_node=2,
control_dof=1,
target_disp=PUSH_TARGET,
step_size=PUSH_STEP,
base_nodes=[1],
system="BandGeneral",
constraints="Plain",
algorithm="Newton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=6,
),
TransientCase(
id=3,
name="Earthquake",
preload_case_ids=[1],
pattern_ids=[400],
dt=ANALYSIS_DT,
n_steps=ANALYSIS_STEPS,
system="SparseGeneral",
constraints="Transformation",
integrator="Newmark",
integrator_params=(0.5, 0.25),
algorithm="ModifiedNewton",
test="EnergyIncr",
tolerance=1e-8,
max_iter=10,
rayleigh_mode1_damping=DAMPING_RATIO,
),
],
)
def main() -> None:
project = build_ex2c_canti2d_inelastic_fiber_section()
project.validate_references()
gm = next(ts for ts in project.time_series if ts.id == 400)
print(f"Built '{project.meta.name}'")
print(
f" LCol={L_COL}, ACol={A_COL:.1f}, Iz={IZ_COL:.1f}, "
f"bars/layer={NUM_BARS_COL}, bar area={BAR_AREA_COL}"
)
print(f" Gravity + pushover + earthquake cases: {len(project.analyses)}")
print(f" Ground motion file: {GROUND_MOTION_FILE.name}")
print(f" Reference Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
print(f" Record points: {len(gm.values)}, dt = {GROUND_DT}s, factor = {GROUND_FACTOR}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,138 @@
"""OpenSees Example 3. Cantilever Column with units: elastic build.
This example follows the separated-build/analysis style of the Tcl
tutorial. Here we package the elastic build variant together with the
shared gravity, pushover, and uniform-earthquake analyses.
"""
from __future__ import annotations
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
StaticCase,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
INCH = 1.0
KIP = 1.0
SEC = 1.0
FT = 12.0 * INCH
KSI = KIP / INCH**2
PSI = KSI / 1000.0
G_ACCEL = 32.2 * FT / SEC**2
L_COL = 36.0 * FT
WEIGHT = 2000.0 * KIP
H_COL = 5.0 * FT
B_COL = 5.0 * FT
P_COL = WEIGHT
MASS = P_COL / G_ACCEL
A_COL = B_COL * H_COL
IZ_COL = (1.0 / 12.0) * B_COL * H_COL**3
FC = -4.0 * KSI
E_C = 57.0 * KSI * math.sqrt(-FC / PSI)
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.05 * L_COL
PUSH_STEP = 0.001 * L_COL
H_LOAD = WEIGHT
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.01
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_BUILD_TCL = _ROOT / "data" / "Ex3.Canti2D.build.ElasticElement.tcl.txt"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex3.Canti2D.analyze.Static.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex3.Canti2D.analyze.Dynamic.EQ.Uniform.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex3_canti2d_elastic_element() -> Project:
values = _ground_motion_values()
return Project(
meta=ProjectMeta(
name="OpenSees Ex 3 - Cantilever (Elastic Build)",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description=(
"Example 3 elastic cantilever build with unit-scaled geometry "
"and shared push / uniform-EQ analysis files."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2,
ndf=3,
nodes=[
Node(id=1, name="Base", coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=2, name="Top", coords=(0.0, L_COL, 0.0), mass=(MASS, 1.0e-9, 0.0, 0.0, 0.0, 0.0)),
],
sections=[
ElasticSection(id=1, name="Elastic-Column", E=E_C, A=A_COL, Iz=IZ_COL, Iy=IZ_COL, G=1.0, J=1.0),
],
elements=[
ElasticBeamColumn(id=1, name="Column", nodes=(1, 2), section_id=1, geom_transf="Linear"),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=200, name="Lateral"),
PathTimeSeries(id=400, name="BM68elc", dt=GROUND_DT, factor=GROUND_FACTOR, values=values, file_path=str(GROUND_MOTION_FILE.name)),
],
load_patterns=[
PlainLoadPattern(id=1, name="Gravity", time_series_id=1, nodal_loads=[NodalLoad(node_id=2, forces=(0.0, -P_COL, 0.0, 0.0, 0.0, 0.0))]),
PlainLoadPattern(id=200, name="Pushover-X", time_series_id=200, nodal_loads=[NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0))]),
UniformExcitationPattern(id=400, name="GroundMotion-X", direction=1, accel_series_id=400),
],
analyses=[
StaticCase(id=1, name="Gravity", pattern_ids=[1], n_steps=N_GRAVITY, load_factor_increment=GRAVITY_STEP, system="BandGeneral", constraints="Plain", integrator="LoadControl", algorithm="Newton", test="NormDispIncr", tolerance=1e-8, max_iter=6),
PushoverCase(id=2, name="Push", preload_case_ids=[1], pattern_ids=[200], control_node=2, control_dof=1, target_disp=PUSH_TARGET, step_size=PUSH_STEP, base_nodes=[1], system="BandGeneral", constraints="Plain", algorithm="Newton", test="EnergyIncr", tolerance=1e-8, max_iter=6),
TransientCase(id=3, name="Earthquake", preload_case_ids=[1], pattern_ids=[400], dt=ANALYSIS_DT, n_steps=ANALYSIS_STEPS, system="SparseGeneral", constraints="Transformation", integrator="Newmark", integrator_params=(0.5, 0.25), algorithm="ModifiedNewton", test="EnergyIncr", tolerance=1e-8, max_iter=10, rayleigh_mode1_damping=DAMPING_RATIO),
],
)
def main() -> None:
project = build_ex3_canti2d_elastic_element()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Build Tcl: {REFERENCE_BUILD_TCL.name}")
print(f" Analysis Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,169 @@
"""OpenSees Example 3. Cantilever Column with units: fiber-section build."""
from __future__ import annotations
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
Concrete02,
FiberSection,
ForceBeamColumn,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
RectangularPatch,
StaticCase,
Steel02,
StraightLayer,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
INCH = 1.0
KIP = 1.0
SEC = 1.0
FT = 12.0 * INCH
KSI = KIP / INCH**2
PSI = KSI / 1000.0
IN2 = INCH * INCH
G_ACCEL = 32.2 * FT / SEC**2
L_COL = 36.0 * FT
WEIGHT = 2000.0 * KIP
H_COL = 5.0 * FT
B_COL = 5.0 * FT
P_COL = WEIGHT
MASS = P_COL / G_ACCEL
A_COL = B_COL * H_COL
IZ_COL = (1.0 / 12.0) * B_COL * H_COL**3
COVER_COL = 5.0 * INCH
NUM_BARS_COL = 20
BAR_AREA_COL = 2.25 * IN2
FC = -4.0 * KSI
EC = 57.0 * KSI * math.sqrt(-FC / PSI)
FC1U = FC
EPS1U = -0.003
FC2U = 0.2 * FC1U
EPS2U = -0.01
LAMBDA = 0.1
FTU = -0.14 * FC1U
ETS = FTU / 0.002
FY = 66.8 * KSI
ES = 29000.0 * KSI
BS = 0.01
R0 = 18.0
CR1 = 0.925
CR2 = 0.15
NUM_INT_PTS = 5
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.05 * L_COL
PUSH_STEP = 0.001 * L_COL
H_LOAD = WEIGHT
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.01
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_BUILD_TCL = _ROOT / "data" / "Ex3.Canti2D.build.InelasticFiberSection.tcl.txt"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex3.Canti2D.analyze.Static.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex3.Canti2D.analyze.Dynamic.EQ.Uniform.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex3_canti2d_inelastic_fiber_section() -> Project:
values = _ground_motion_values()
cover_y = H_COL / 2.0
cover_z = B_COL / 2.0
core_y = cover_y - COVER_COL
core_z = cover_z - COVER_COL
return Project(
meta=ProjectMeta(
name="OpenSees Ex 3 - Cantilever (Fiber Section Build)",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description="Example 3 cantilever with inelastic fiber section and shared analysis files.",
units=UnitSystem.US_IN_KIP,
),
ndm=2, ndf=3,
nodes=[
Node(id=1, name="Base", coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=2, name="Top", coords=(0.0, L_COL, 0.0), mass=(MASS, 1.0e-9, 0.0, 0.0, 0.0, 0.0)),
],
materials=[
Concrete02(id=1, name="Cover-Concrete", fpc=FC1U, epsc0=EPS1U, fpcu=FC2U, epsU=EPS2U, lambda_=LAMBDA, ft=FTU, Ets=ETS),
Steel02(id=2, name="Rebar-Steel", Fy=FY, E0=ES, b=BS, R0=R0, cR1=CR1, cR2=CR2),
],
sections=[
FiberSection(
id=1,
name="RC-Fiber-Section",
patches=[RectangularPatch(material_id=1, n_fib_y=16, n_fib_z=4, y_i=-cover_y, z_i=-cover_z, y_j=cover_y, z_j=cover_z)],
layers=[
StraightLayer(material_id=2, n_bars=NUM_BARS_COL, bar_area=BAR_AREA_COL, y_start=-core_y, z_start=core_z, y_end=-core_y, z_end=-core_z),
StraightLayer(material_id=2, n_bars=NUM_BARS_COL, bar_area=BAR_AREA_COL, y_start=core_y, z_start=core_z, y_end=core_y, z_end=-core_z),
],
),
],
elements=[
ForceBeamColumn(id=1, name="Column", nodes=(1, 2), section_id=1, integration_points=NUM_INT_PTS, geom_transf="Linear"),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=200, name="Lateral"),
PathTimeSeries(id=400, name="BM68elc", dt=GROUND_DT, factor=GROUND_FACTOR, values=values, file_path=str(GROUND_MOTION_FILE.name)),
],
load_patterns=[
PlainLoadPattern(id=1, name="Gravity", time_series_id=1, nodal_loads=[NodalLoad(node_id=2, forces=(0.0, -P_COL, 0.0, 0.0, 0.0, 0.0))]),
PlainLoadPattern(id=200, name="Pushover-X", time_series_id=200, nodal_loads=[NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0))]),
UniformExcitationPattern(id=400, name="GroundMotion-X", direction=1, accel_series_id=400),
],
analyses=[
StaticCase(id=1, name="Gravity", pattern_ids=[1], n_steps=N_GRAVITY, load_factor_increment=GRAVITY_STEP, system="BandGeneral", constraints="Plain", integrator="LoadControl", algorithm="Newton", test="NormDispIncr", tolerance=1e-8, max_iter=6),
PushoverCase(id=2, name="Push", preload_case_ids=[1], pattern_ids=[200], control_node=2, control_dof=1, target_disp=PUSH_TARGET, step_size=PUSH_STEP, base_nodes=[1], system="BandGeneral", constraints="Plain", algorithm="Newton", test="EnergyIncr", tolerance=1e-8, max_iter=6),
TransientCase(id=3, name="Earthquake", preload_case_ids=[1], pattern_ids=[400], dt=ANALYSIS_DT, n_steps=ANALYSIS_STEPS, system="SparseGeneral", constraints="Transformation", integrator="Newmark", integrator_params=(0.5, 0.25), algorithm="ModifiedNewton", test="EnergyIncr", tolerance=1e-8, max_iter=10, rayleigh_mode1_damping=DAMPING_RATIO),
],
)
def main() -> None:
project = build_ex3_canti2d_inelastic_fiber_section()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Build Tcl: {REFERENCE_BUILD_TCL.name}")
print(f" Analysis Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,142 @@
"""OpenSees Example 3. Cantilever Column with units: uniaxial inelastic section build."""
from __future__ import annotations
from pathlib import Path
import math
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from otko.core import ( # noqa: E402
AggregatorDOF,
ElasticUniaxial,
ForceBeamColumn,
LinearTimeSeries,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
SectionAggregator,
StaticCase,
Steel01,
TransientCase,
UniformExcitationPattern,
UnitSystem,
)
from otko.services import load_project, save_project # noqa: E402
from otko.services.peer_record import parse_plain_values # noqa: E402
INCH = 1.0
KIP = 1.0
SEC = 1.0
FT = 12.0 * INCH
KSI = KIP / INCH**2
PSI = KSI / 1000.0
G_ACCEL = 32.2 * FT / SEC**2
L_COL = 36.0 * FT
WEIGHT = 2000.0 * KIP
H_COL = 5.0 * FT
B_COL = 5.0 * FT
P_COL = WEIGHT
MASS = P_COL / G_ACCEL
A_COL = B_COL * H_COL
IZ_COL = (1.0 / 12.0) * B_COL * H_COL**3
FC = -4.0 * KSI
E_C = 57.0 * KSI * math.sqrt(-FC / PSI)
EA_COL = E_C * A_COL
MY_COL = 130000.0 * KIP * INCH
PHI_Y_COL = 0.65e-4 / INCH
EI_COL_CRACK = MY_COL / PHI_Y_COL
HARDENING_RATIO = 0.01
NUM_INT_PTS = 5
N_GRAVITY = 10
GRAVITY_STEP = 1.0 / N_GRAVITY
PUSH_TARGET = 0.05 * L_COL
PUSH_STEP = 0.001 * L_COL
H_LOAD = WEIGHT
GROUND_DT = 0.01
GROUND_FACTOR = 1.0
ANALYSIS_DT = 0.01
ANALYSIS_STEPS = 1000
DAMPING_RATIO = 0.02
_ROOT = Path(__file__).resolve().parent
GROUND_MOTION_FILE = _ROOT / "data" / "BM68elc.acc"
REFERENCE_BUILD_TCL = _ROOT / "data" / "Ex3.Canti2D.build.InelasticSection.tcl.txt"
REFERENCE_PUSH_TCL = _ROOT / "data" / "Ex3.Canti2D.analyze.Static.Push.tcl.txt"
REFERENCE_EQ_TCL = _ROOT / "data" / "Ex3.Canti2D.analyze.Dynamic.EQ.Uniform.tcl.txt"
def _ground_motion_values() -> list[float]:
return parse_plain_values(GROUND_MOTION_FILE)
def build_ex3_canti2d_inelastic_section() -> Project:
values = _ground_motion_values()
return Project(
meta=ProjectMeta(
name="OpenSees Ex 3 - Cantilever (Inelastic Section Build)",
author="OpenSees Wiki / Silvia Mazzoni & Frank McKenna",
description="Example 3 cantilever with aggregated uniaxial inelastic section and shared analysis files.",
units=UnitSystem.US_IN_KIP,
),
ndm=2, ndf=3,
nodes=[
Node(id=1, name="Base", coords=(0.0, 0.0, 0.0), restraint=(True, True, False, False, False, True)),
Node(id=2, name="Top", coords=(0.0, L_COL, 0.0), mass=(MASS, 1.0e-9, 0.0, 0.0, 0.0, 0.0)),
],
materials=[
Steel01(id=2, name="Flexural-Steel01", Fy=MY_COL, E0=EI_COL_CRACK, b=HARDENING_RATIO),
ElasticUniaxial(id=3, name="Axial-Elastic", E=EA_COL),
],
sections=[
SectionAggregator(id=1, name="Col-Section", pairings=[AggregatorDOF(material_id=3, dof="P"), AggregatorDOF(material_id=2, dof="Mz")]),
],
elements=[
ForceBeamColumn(id=1, name="Column", nodes=(1, 2), section_id=1, integration_points=NUM_INT_PTS, geom_transf="Linear"),
],
time_series=[
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=200, name="Lateral"),
PathTimeSeries(id=400, name="BM68elc", dt=GROUND_DT, factor=GROUND_FACTOR, values=values, file_path=str(GROUND_MOTION_FILE.name)),
],
load_patterns=[
PlainLoadPattern(id=1, name="Gravity", time_series_id=1, nodal_loads=[NodalLoad(node_id=2, forces=(0.0, -P_COL, 0.0, 0.0, 0.0, 0.0))]),
PlainLoadPattern(id=200, name="Pushover-X", time_series_id=200, nodal_loads=[NodalLoad(node_id=2, forces=(H_LOAD, 0.0, 0.0, 0.0, 0.0, 0.0))]),
UniformExcitationPattern(id=400, name="GroundMotion-X", direction=1, accel_series_id=400),
],
analyses=[
StaticCase(id=1, name="Gravity", pattern_ids=[1], n_steps=N_GRAVITY, load_factor_increment=GRAVITY_STEP, system="BandGeneral", constraints="Plain", integrator="LoadControl", algorithm="Newton", test="NormDispIncr", tolerance=1e-8, max_iter=6),
PushoverCase(id=2, name="Push", preload_case_ids=[1], pattern_ids=[200], control_node=2, control_dof=1, target_disp=PUSH_TARGET, step_size=PUSH_STEP, base_nodes=[1], system="BandGeneral", constraints="Plain", algorithm="Newton", test="EnergyIncr", tolerance=1e-8, max_iter=6),
TransientCase(id=3, name="Earthquake", preload_case_ids=[1], pattern_ids=[400], dt=ANALYSIS_DT, n_steps=ANALYSIS_STEPS, system="SparseGeneral", constraints="Transformation", integrator="Newmark", integrator_params=(0.5, 0.25), algorithm="ModifiedNewton", test="EnergyIncr", tolerance=1e-8, max_iter=10, rayleigh_mode1_damping=DAMPING_RATIO),
],
)
def main() -> None:
project = build_ex3_canti2d_inelastic_section()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Build Tcl: {REFERENCE_BUILD_TCL.name}")
print(f" Analysis Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_EQ_TCL.name}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
"""OpenSees Example 4. Portal Frame: elastic build."""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parent))
from otko.services import load_project, save_project # noqa: E402
if __package__:
from ._ex4_portal2d_common import ( # noqa: E402
ANALYSIS_DT,
ANALYSIS_STEPS,
ELASTIC_VARIANT,
PUSH_STEP,
PUSH_TARGET,
REFERENCE_PUSH_TCL,
REFERENCE_SINE_TCL,
build_ex4_portal2d_elastic_element,
)
else:
from _ex4_portal2d_common import ( # noqa: E402
ANALYSIS_DT,
ANALYSIS_STEPS,
ELASTIC_VARIANT,
PUSH_STEP,
PUSH_TARGET,
REFERENCE_PUSH_TCL,
REFERENCE_SINE_TCL,
build_ex4_portal2d_elastic_element,
)
def main() -> None:
project = build_ex4_portal2d_elastic_element()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Build Tcl: {ELASTIC_VARIANT.build_tcl_name}")
print(f" Analysis Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_SINE_TCL.name}")
print(f" Pushover target = {PUSH_TARGET}, step = {PUSH_STEP}")
print(f" Sine transient: dt = {ANALYSIS_DT}s, steps = {ANALYSIS_STEPS}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
"""OpenSees Example 4. Portal Frame: fiber-section build."""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parent))
from otko.services import load_project, save_project # noqa: E402
if __package__:
from ._ex4_portal2d_common import ( # noqa: E402
ANALYSIS_DT,
ANALYSIS_STEPS,
FIBER_VARIANT,
PUSH_STEP,
PUSH_TARGET,
REFERENCE_PUSH_TCL,
REFERENCE_SINE_TCL,
build_ex4_portal2d_inelastic_fiber_section,
)
else:
from _ex4_portal2d_common import ( # noqa: E402
ANALYSIS_DT,
ANALYSIS_STEPS,
FIBER_VARIANT,
PUSH_STEP,
PUSH_TARGET,
REFERENCE_PUSH_TCL,
REFERENCE_SINE_TCL,
build_ex4_portal2d_inelastic_fiber_section,
)
def main() -> None:
project = build_ex4_portal2d_inelastic_fiber_section()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Build Tcl: {FIBER_VARIANT.build_tcl_name}")
print(f" Analysis Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_SINE_TCL.name}")
print(f" Pushover target = {PUSH_TARGET}, step = {PUSH_STEP}")
print(f" Sine transient: dt = {ANALYSIS_DT}s, steps = {ANALYSIS_STEPS}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
"""OpenSees Example 4. Portal Frame: inelastic uniaxial-section build."""
from __future__ import annotations
from pathlib import Path
import sys
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parent))
from otko.services import load_project, save_project # noqa: E402
if __package__:
from ._ex4_portal2d_common import ( # noqa: E402
ANALYSIS_DT,
ANALYSIS_STEPS,
INELASTIC_SECTION_VARIANT,
PUSH_STEP,
PUSH_TARGET,
REFERENCE_PUSH_TCL,
REFERENCE_SINE_TCL,
build_ex4_portal2d_inelastic_section,
)
else:
from _ex4_portal2d_common import ( # noqa: E402
ANALYSIS_DT,
ANALYSIS_STEPS,
INELASTIC_SECTION_VARIANT,
PUSH_STEP,
PUSH_TARGET,
REFERENCE_PUSH_TCL,
REFERENCE_SINE_TCL,
build_ex4_portal2d_inelastic_section,
)
def main() -> None:
project = build_ex4_portal2d_inelastic_section()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Build Tcl: {INELASTIC_SECTION_VARIANT.build_tcl_name}")
print(f" Analysis Tcls: {REFERENCE_PUSH_TCL.name}, {REFERENCE_SINE_TCL.name}")
print(f" Pushover target = {PUSH_TARGET}, step = {PUSH_STEP}")
print(f" Sine transient: dt = {ANALYSIS_DT}s, steps = {ANALYSIS_STEPS}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,335 @@
{
"schema_version": 1,
"meta": {
"name": "Moment-Curvature (OpenSees Ex 2)",
"description": "RC column fibre section — constant axial P + DisplacementControl curvature pushover. Kip-in-ksi units throughout.",
"author": "OpenSees Examples Manual",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [
{
"id": "X1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"y_grid_lines": [
{
"id": "Y1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"z_grid_lines": [
{
"id": "Z1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Support",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "Crown",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
true,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "Core-Conc",
"type": "Concrete01",
"fpc": -6.0,
"epsc0": -0.004,
"fpcu": -5.0,
"epsU": -0.014
},
{
"id": 2,
"name": "Cover-Conc",
"type": "Concrete01",
"fpc": -5.0,
"epsc0": -0.002,
"fpcu": 0.0,
"epsU": -0.006
},
{
"id": 3,
"name": "Steel-60",
"type": "Steel01",
"Fy": 60.0,
"E0": 30000.0,
"b": 0.01,
"a1": null,
"a2": null,
"a3": null,
"a4": null
}
],
"sections": [
{
"id": 1,
"name": "RC-Column",
"type": "FiberSection",
"GJ": null,
"patches": [
{
"kind": "rect",
"material_id": 1,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -10.5,
"z_i": -6.0,
"y_j": 10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": 6.0,
"y_j": 12.0,
"z_j": 7.5
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -7.5,
"y_j": 12.0,
"z_j": -6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -6.0,
"y_j": -10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": 10.5,
"z_i": -6.0,
"y_j": 12.0,
"z_j": 6.0
}
],
"layers": [
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": 10.5,
"z_start": 6.0,
"y_end": 10.5,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 2,
"bar_area": 0.6,
"y_start": 0.0,
"z_start": 6.0,
"y_end": 0.0,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": -10.5,
"z_start": 6.0,
"y_end": -10.5,
"z_end": -6.0
}
],
"fibres": []
}
],
"elements": [
{
"id": 1,
"name": "MK-Link",
"type": "ZeroLengthSection",
"nodes": [
1,
2
],
"section_id": 1
}
],
"time_series": [
{
"id": 1,
"name": "AxialP",
"type": "Constant",
"factor": 1.0
},
{
"id": 2,
"name": "RefMoment",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "AxialP",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 2,
"forces": [
-180.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
},
{
"id": 2,
"name": "RefMoment",
"type": "Plain",
"time_series_id": 2,
"nodal_loads": [
{
"node_id": 2,
"forces": [
0.0,
0.0,
0.0,
0.0,
0.0,
1.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "MK",
"type": "Pushover",
"pattern_ids": [
1,
2
],
"control_node": 2,
"control_dof": 3,
"target_disp": 0.0019047619047619052,
"step_size": 1.9047619047619053e-05,
"base_nodes": [
1
],
"system": "BandGeneral",
"constraints": "Plain",
"algorithm": "Newton",
"test": "NormUnbalance",
"tolerance": 1e-09,
"max_iter": 25
}
]
}

View file

@ -0,0 +1,249 @@
"""Moment-Curvature Example — OpenSees Examples Manual, Example 2.
Reinforced-concrete column cross-section, fibre discretisation, axial
preload + monotonic moment pushover. Mirrors the OpenSees Tcl script
at https://opensees.berkeley.edu/wiki/index.php?title=Moment_Curvature_Example
Model
-----
Two coincident nodes linked by a ``zeroLengthSection`` carrying the
RC fibre section. Node 1 is fully restrained; node 2 is free in Ux
(so axial can shorten) and Rz (the curvature DOF). A constant axial
load P = -180 kip is applied first via LoadControl(0); then a
linear reference moment pattern (Mz = 1 kip·in) is added and
DisplacementControl on DOF 3 ramps the curvature to μ·Ky where
μ = 15 and Ky is the elastic yield curvature estimate.
Units: kip, in, ksi (UnitSystem.US_IN_KIP).
GUI walkthrough: File → Open → moment_curvature.osmodel, Options →
Set Display Units → US (in, kip, kip·s²/in, s, ksi), Analyze → Run →
MK, Display → Show Pushover Curve.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
Concrete01,
ConstantTimeSeries,
CoordinateGridSystem,
FiberSection,
GridSystem,
LinearTimeSeries,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
RectangularPatch,
Steel01,
StraightLayer,
UnitSystem,
ZeroLengthSectionElement,
make_grid_lines,
)
from otko.services import load_project, save_project
# Cross-section parameters (kip-in-ksi, from the Tcl example).
COL_WIDTH = 15.0 # z-direction dimension
COL_DEPTH = 24.0 # y-direction dimension
COVER = 1.5
AS_BAR = 0.60 # area of one #7 rebar
FY = 60.0 # steel yield stress, ksi
E_STEEL = 30000.0 # steel Young's modulus, ksi
HARDENING = 0.01 # strain-hardening ratio
P_AXIAL = -180.0 # kip, compression
MU = 15 # target curvature ductility
NUM_INCR = 100 # DisplacementControl increments
def build_moment_curvature() -> Project:
"""Build the OpenSees Example 2 Moment-Curvature project."""
y1 = COL_DEPTH / 2.0 # 12
z1 = COL_WIDTH / 2.0 # 7.5
d = COL_DEPTH - COVER # 22.5
# Yield curvature estimate, assumed elastic + top/bottom steel only.
eps_y = FY / E_STEEL # 0.002
ky = eps_y / (0.7 * d) # ≈ 1.27e-4
max_k = ky * MU # ≈ 1.905e-3
d_k = max_k / NUM_INCR # per-step curvature increment
return Project(
meta=ProjectMeta(
name="Moment-Curvature (OpenSees Ex 2)",
author="OpenSees Examples Manual",
description=(
"RC column fibre section — constant axial P + DisplacementControl "
"curvature pushover. Kip-in-ksi units throughout."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2, ndf=3,
coord_systems=[
# A tiny grid at the section origin so the two coincident
# nodes have a visual anchor in the canvas.
CoordinateGridSystem(
name="Global",
grid=GridSystem(
x_grid_lines=make_grid_lines("X", [0.0]),
y_grid_lines=make_grid_lines("Y", [0.0]),
z_grid_lines=make_grid_lines("Z", [0.0]),
),
),
],
nodes=[
# Node 1 — fully clamped.
Node(id=1, name="Support",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True)),
# Node 2 — free in Ux and Rz (axial + curvature).
Node(id=2, name="Crown",
coords=(0.0, 0.0, 0.0),
restraint=(False, True, False, False, False, False)),
],
materials=[
# Core concrete — confined (tag 1).
Concrete01(id=1, name="Core-Conc",
fpc=-6.0, epsc0=-0.004,
fpcu=-5.0, epsU=-0.014),
# Cover concrete — unconfined (tag 2).
Concrete01(id=2, name="Cover-Conc",
fpc=-5.0, epsc0=-0.002,
fpcu=0.0, epsU=-0.006),
# Reinforcing steel — bilinear hardening (tag 3).
Steel01(id=3, name="Steel-60",
Fy=FY, E0=E_STEEL, b=HARDENING),
],
sections=[
FiberSection(
id=1, name="RC-Column",
patches=[
# Core — confined concrete inside the rebar ring.
RectangularPatch(
material_id=1, n_fib_y=10, n_fib_z=1,
y_i=COVER - y1, z_i=COVER - z1,
y_j=y1 - COVER, z_j=z1 - COVER,
),
# Top cover (unconfined).
RectangularPatch(
material_id=2, n_fib_y=10, n_fib_z=1,
y_i=-y1, z_i=z1 - COVER,
y_j=y1, z_j=z1,
),
# Bottom cover.
RectangularPatch(
material_id=2, n_fib_y=10, n_fib_z=1,
y_i=-y1, z_i=-z1,
y_j=y1, z_j=COVER - z1,
),
# Left cover.
RectangularPatch(
material_id=2, n_fib_y=2, n_fib_z=1,
y_i=-y1, z_i=COVER - z1,
y_j=COVER - y1, z_j=z1 - COVER,
),
# Right cover.
RectangularPatch(
material_id=2, n_fib_y=2, n_fib_z=1,
y_i=y1 - COVER, z_i=COVER - z1,
y_j=y1, z_j=z1 - COVER,
),
],
layers=[
# Top rebar (3 × #7).
StraightLayer(
material_id=3, n_bars=3, bar_area=AS_BAR,
y_start=y1 - COVER, z_start=z1 - COVER,
y_end=y1 - COVER, z_end=COVER - z1,
),
# Middle rebar (2 × #7).
StraightLayer(
material_id=3, n_bars=2, bar_area=AS_BAR,
y_start=0.0, z_start=z1 - COVER,
y_end=0.0, z_end=COVER - z1,
),
# Bottom rebar (3 × #7).
StraightLayer(
material_id=3, n_bars=3, bar_area=AS_BAR,
y_start=COVER - y1, z_start=z1 - COVER,
y_end=COVER - y1, z_end=COVER - z1,
),
],
),
],
elements=[
ZeroLengthSectionElement(
id=1, name="MK-Link",
nodes=(1, 2), section_id=1,
),
],
time_series=[
ConstantTimeSeries(id=1, name="AxialP"),
LinearTimeSeries(id=2, name="RefMoment"),
],
load_patterns=[
# Constant axial preload at node 2 — Fx = P (compression).
PlainLoadPattern(
id=1, name="AxialP",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=2,
forces=(P_AXIAL, 0, 0, 0, 0, 0)),
],
),
# Linear reference moment — Mz = 1.0, DisplacementControl
# scales this as it ramps curvature.
PlainLoadPattern(
id=2, name="RefMoment",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=2,
forces=(0, 0, 0, 0, 0, 1.0)),
],
),
],
analyses=[
PushoverCase(
id=1, name="MK",
pattern_ids=[1, 2],
control_node=2, control_dof=3, # Rz = curvature
target_disp=max_k,
step_size=d_k,
base_nodes=[1],
test="NormUnbalance",
tolerance=1e-9, max_iter=25,
),
],
)
def main() -> None:
project = build_moment_curvature()
project.validate_references()
y1 = COL_DEPTH / 2.0
eps_y = FY / E_STEEL
ky = eps_y / (0.7 * (COL_DEPTH - COVER))
print(f"Built '{project.meta.name}'")
print(f" ndm={project.ndm}, ndf={project.ndf}, units={project.meta.units.value}")
print(f" Estimated yield curvature Ky = {ky:.4e} 1/in")
print(f" Target (mu * Ky) = {ky * MU:.4e} 1/in (mu = {MU})")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,516 @@
{
"schema_version": 1,
"meta": {
"name": "Portal Frame",
"description": "",
"author": "Ozan",
"units": "SI (m, N, kg, s, Pa)"
},
"ndm": 3,
"ndf": 6,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base-L",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
true,
true,
true
]
},
{
"id": 2,
"name": "Base-R",
"coords": [
6.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
true,
true,
true
]
},
{
"id": 3,
"name": "Top-L",
"coords": [
0.0,
0.0,
3.0
],
"mass": [
5000.0,
5000.0,
5000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "Top-R",
"coords": [
6.0,
0.0,
3.0
],
"mass": [
5000.0,
5000.0,
5000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "S420",
"type": "Steel01",
"Fy": 420000000.0,
"E0": 200000000000.0,
"b": 0.01,
"a1": null,
"a2": null,
"a3": null,
"a4": null
}
],
"sections": [
{
"id": 1,
"name": "W14x90",
"type": "ElasticSection",
"E": 200000000000.0,
"A": 0.017,
"Iz": 0.000416,
"Iy": 0.000129,
"G": 80000000000.0,
"J": 2.04e-06
}
],
"elements": [
{
"id": 1,
"name": "Col-L",
"type": "ElasticBeamColumn",
"nodes": [
1,
3
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 2,
"name": "Col-R",
"type": "ElasticBeamColumn",
"nodes": [
2,
4
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
},
{
"id": 3,
"name": "Beam",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Ramp",
"type": "Linear",
"factor": 1.0
},
{
"id": 2,
"name": "SineGust",
"type": "Path",
"factor": 1.0,
"dt": 0.01,
"times": null,
"values": [
0.0,
0.06279051952931337,
0.12533323356430426,
0.1873813145857246,
0.2486898871648548,
0.3090169943749474,
0.3681245526846779,
0.42577929156507266,
0.4817536741017153,
0.5358267949789967,
0.5877852522924731,
0.6374239897486896,
0.6845471059286886,
0.7289686274214116,
0.7705132427757893,
0.8090169943749473,
0.8443279255020151,
0.8763066800438637,
0.9048270524660196,
0.9297764858882513,
0.9510565162951535,
0.9685831611286311,
0.9822872507286886,
0.9921147013144778,
0.9980267284282716,
1.0,
0.9980267284282716,
0.9921147013144779,
0.9822872507286887,
0.9685831611286311,
0.9510565162951536,
0.9297764858882515,
0.9048270524660195,
0.8763066800438635,
0.844327925502015,
0.8090169943749475,
0.7705132427757893,
0.7289686274214114,
0.6845471059286888,
0.6374239897486899,
0.5877852522924732,
0.535826794978997,
0.4817536741017152,
0.4257792915650729,
0.36812455268467814,
0.3090169943749475,
0.24868988716485524,
0.18738131458572457,
0.12533323356430454,
0.06279051952931358,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"file_path": null
}
],
"load_patterns": [
{
"id": 1,
"name": "Lateral",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 3,
"forces": [
50000.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
},
{
"id": 2,
"name": "SineGustPattern",
"type": "Plain",
"time_series_id": 2,
"nodal_loads": [
{
"node_id": 3,
"forces": [
100000.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Linear-Static",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 1,
"load_factor_increment": 1.0,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "LoadControl",
"algorithm": "Linear",
"test": "NormDispIncr",
"tolerance": 1e-08,
"max_iter": 25
},
{
"id": 2,
"name": "Modal-3",
"type": "Modal",
"n_modes": 3,
"solver": "genBandArpack"
},
{
"id": 3,
"name": "Sine-Gust-2s",
"type": "Transient",
"pattern_ids": [
2
],
"dt": 0.01,
"n_steps": 200,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "Newmark",
"integrator_params": [
0.5,
0.25
],
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-06,
"max_iter": 25,
"rayleigh_alpha_m": 0.0,
"rayleigh_beta_k": 0.0
}
]
}

138
examples/portal_frame.py Normal file
View file

@ -0,0 +1,138 @@
"""Build a 3D portal frame with Static + Modal + Transient cases.
Run from the repository root:
python examples/portal_frame.py
Produces ``examples/portal_frame.osmodel`` — open it from the GUI's
File → Open menu, then exercise:
- **Static** case → Display → Show Deformed Shape, Show Force Diagram
- **Modal** case → Display → Animate Mode Shape
- **Transient** case → Display → Time-History Plot, Hysteresis Plot
The model is a single-bay portal frame loaded laterally:
Top-L ──── Beam ──── Top-R z
│ │ │
│ │ │
Col-L Col-R └── x
│ │
Base-L Base-R (y = 0; planar in x-z)
Mass is lumped at the top nodes so modal/transient solvers have
non-singular mass matrices.
"""
from __future__ import annotations
import math
from pathlib import Path
from otko.core import (
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
ModalCase,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
StaticCase,
Steel01,
TransientCase,
UnitSystem,
)
from otko.services import load_project, save_project
def _sine_pulse_factors() -> list[float]:
"""One half-cycle sine over the first 0.5s, then zero for the rest of 2s."""
n_pulse = 50 # 0.5 s @ 100 Hz
n_total = 200
return [math.sin(math.pi * i / n_pulse) if i < n_pulse else 0.0
for i in range(n_total)]
def build_portal_frame() -> Project:
"""A two-column, one-beam steel portal frame with static + dynamic cases."""
return Project(
meta=ProjectMeta(name="Portal Frame", author="Ozan", units=UnitSystem.SI_M_N),
ndm=3,
ndf=6,
nodes=[
Node(id=1, name="Base-L", coords=(0.0, 0.0, 0.0), restraint=(True,) * 6),
Node(id=2, name="Base-R", coords=(6.0, 0.0, 0.0), restraint=(True,) * 6),
Node(id=3, name="Top-L", coords=(0.0, 0.0, 3.0),
mass=(5000.0, 5000.0, 5000.0, 0.0, 0.0, 0.0)),
Node(id=4, name="Top-R", coords=(6.0, 0.0, 3.0),
mass=(5000.0, 5000.0, 5000.0, 0.0, 0.0, 0.0)),
],
materials=[
Steel01(id=1, name="S420", Fy=420e6, E0=200e9, b=0.01),
],
sections=[
ElasticSection(
id=1, name="W14x90",
E=200e9, A=0.017,
Iz=4.16e-4, Iy=1.29e-4,
G=80e9, J=2.04e-6,
),
],
elements=[
ElasticBeamColumn(id=1, name="Col-L", nodes=(1, 3), section_id=1),
ElasticBeamColumn(id=2, name="Col-R", nodes=(2, 4), section_id=1),
ElasticBeamColumn(id=3, name="Beam", nodes=(3, 4), section_id=1),
],
time_series=[
LinearTimeSeries(id=1, name="Ramp"),
PathTimeSeries(
id=2, name="SineGust",
values=_sine_pulse_factors(),
dt=0.01,
),
],
load_patterns=[
# Pattern 1: 50 kN lateral push at top-left for Static.
PlainLoadPattern(
id=1, name="Lateral",
time_series_id=1,
nodal_loads=[NodalLoad(node_id=3, forces=(50_000.0, 0, 0, 0, 0, 0))],
),
# Pattern 2: 100 kN sine pulse at top-left for Transient.
PlainLoadPattern(
id=2, name="SineGustPattern",
time_series_id=2,
nodal_loads=[NodalLoad(node_id=3, forces=(100_000.0, 0, 0, 0, 0, 0))],
),
],
analyses=[
StaticCase(id=1, name="Linear-Static", pattern_ids=[1]),
ModalCase(id=2, name="Modal-3", n_modes=3),
TransientCase(
id=3, name="Sine-Gust-2s",
pattern_ids=[2],
dt=0.01, n_steps=200, # 2 seconds @ 100 Hz
),
],
)
def main() -> None:
project = build_portal_frame()
project.validate_references()
print(f"Built '{project.meta.name}' — {len(project.nodes)} nodes, "
f"{len(project.elements)} elements, {len(project.analyses)} cases.")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,353 @@
{
"schema_version": 1,
"meta": {
"name": "Portal Pushover",
"description": "",
"author": "Ozan",
"units": "SI (m, N, kg, s, Pa)"
},
"ndm": 3,
"ndf": 6,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base-L",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
true,
true,
true
]
},
{
"id": 2,
"name": "Base-R",
"coords": [
6.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
true,
true,
true
]
},
{
"id": 3,
"name": "Top-L",
"coords": [
0.0,
0.0,
3.0
],
"mass": [
10000.0,
10000.0,
10000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "Top-R",
"coords": [
6.0,
0.0,
3.0
],
"mass": [
10000.0,
10000.0,
10000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "C30",
"type": "Concrete01",
"fpc": -30000000.0,
"epsc0": -0.002,
"fpcu": -0.0,
"epsU": -0.005
},
{
"id": 2,
"name": "S420",
"type": "Steel01",
"Fy": 420000000.0,
"E0": 200000000000.0,
"b": 0.01,
"a1": null,
"a2": null,
"a3": null,
"a4": null
},
{
"id": 3,
"name": "GJ-spring",
"type": "Elastic",
"E": 28800000.0,
"eta": 0.0,
"Eneg": null
}
],
"sections": [
{
"id": 1,
"name": "RC-Column",
"type": "FiberSection",
"GJ": null,
"patches": [
{
"kind": "rect",
"material_id": 1,
"n_fib_y": 8,
"n_fib_z": 10,
"y_i": -0.10999999999999999,
"z_i": -0.16,
"y_j": 0.10999999999999999,
"z_j": 0.16
}
],
"layers": [
{
"kind": "straight",
"material_id": 2,
"n_bars": 3,
"bar_area": 0.000314,
"y_start": -0.10999999999999999,
"z_start": -0.16,
"y_end": 0.10999999999999999,
"z_end": -0.16
},
{
"kind": "straight",
"material_id": 2,
"n_bars": 3,
"bar_area": 0.000314,
"y_start": -0.10999999999999999,
"z_start": 0.16,
"y_end": 0.10999999999999999,
"z_end": 0.16
}
],
"fibres": []
},
{
"id": 2,
"name": "Col-Agg",
"type": "SectionAggregator",
"section_id": 1,
"pairings": [
{
"material_id": 3,
"dof": "T"
}
]
},
{
"id": 3,
"name": "W14x90",
"type": "ElasticSection",
"E": 200000000000.0,
"A": 0.017,
"Iz": 0.000416,
"Iy": 0.000129,
"G": 80000000000.0,
"J": 2.04e-06
}
],
"elements": [
{
"id": 1,
"name": "Col-L",
"type": "BeamWithHinges",
"nodes": [
1,
3
],
"section_i_id": 2,
"section_j_id": 2,
"lp_i": 0.4,
"lp_j": 0.01,
"E": 200000000000.0,
"A": 0.017,
"Iz": 0.000416,
"Iy": 0.000129,
"G": 80000000000.0,
"J": 2.04e-06,
"geom_transf": "Linear"
},
{
"id": 2,
"name": "Col-R",
"type": "BeamWithHinges",
"nodes": [
2,
4
],
"section_i_id": 2,
"section_j_id": 2,
"lp_i": 0.4,
"lp_j": 0.01,
"E": 200000000000.0,
"A": 0.017,
"Iz": 0.000416,
"Iy": 0.000129,
"G": 80000000000.0,
"J": 2.04e-06,
"geom_transf": "Linear"
},
{
"id": 3,
"name": "Beam",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 3,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Ramp",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "PushRef",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 3,
"forces": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Push-X",
"type": "Pushover",
"pattern_ids": [
1
],
"control_node": 3,
"control_dof": 1,
"target_disp": 0.15,
"step_size": 0.0005,
"base_nodes": [
1,
2
],
"system": "BandGeneral",
"constraints": "Plain",
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-06,
"max_iter": 25
},
{
"id": 2,
"name": "Modal-3",
"type": "Modal",
"n_modes": 3,
"solver": "genBandArpack"
}
]
}

189
examples/portal_pushover.py Normal file
View file

@ -0,0 +1,189 @@
"""Portal frame with fiber-section plastic hinges and pushover analysis.
A single-bay portal frame where the column bases use FiberSections
(concrete core + rebar layers) wrapped in a SectionAggregator (for
torsion) and assigned to BeamWithHingesElements. The beam and column
tops remain elastic.
Demonstrates the full Phase 9 pipeline:
1. Define concrete + steel uniaxialMaterials
2. Build a FiberSection with a rectangular concrete patch + rebar layers
3. Wrap in a SectionAggregator that adds elastic torsion
4. Assign BeamWithHingesElements to the columns (hinges at base only)
5. Run a monotonic pushover to 0.15 m lateral displacement
6. View the pushover curve and force diagrams
Run:
python examples/portal_pushover.py
Produces ``examples/portal_pushover.osmodel``.
GUI walkthrough:
File → Open → portal_pushover.osmodel
Analyze → Cases → Run "Push-X"
Display → Show Pushover Curve → see yielding + hardening
Display → Show Force Diagram → M3 at the last pushover step
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
AggregatorDOF,
BeamWithHingesElement,
Concrete01,
ElasticBeamColumn,
ElasticSection,
ElasticUniaxial,
FiberSection,
LinearTimeSeries,
ModalCase,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
RectangularPatch,
SectionAggregator,
Steel01,
StraightLayer,
UnitSystem,
)
from otko.services import load_project, save_project
def build_portal_pushover() -> Project:
"""Portal frame: 2 columns (hinge at base) + 1 beam (elastic)."""
H = 3.0 # story height
L = 6.0 # bay width
b = 0.30 # column width (y)
h = 0.40 # column depth (z)
cover = 0.04
return Project(
meta=ProjectMeta(name="Portal Pushover", author="Ozan",
units=UnitSystem.SI_M_N),
ndm=3, ndf=6,
nodes=[
Node(id=1, name="Base-L", coords=(0.0, 0.0, 0.0),
restraint=(True,) * 6),
Node(id=2, name="Base-R", coords=(L, 0.0, 0.0),
restraint=(True,) * 6),
Node(id=3, name="Top-L", coords=(0.0, 0.0, H),
mass=(10_000.0, 10_000.0, 10_000.0, 0.0, 0.0, 0.0)),
Node(id=4, name="Top-R", coords=(L, 0.0, H),
mass=(10_000.0, 10_000.0, 10_000.0, 0.0, 0.0, 0.0)),
],
materials=[
# Concrete: unconfined C30 (fpc negative by convention)
Concrete01(id=1, name="C30",
fpc=-30e6, epsc0=-0.002, fpcu=-0.0, epsU=-0.005),
# Steel rebar: S420 bilinear
Steel01(id=2, name="S420", Fy=420e6, E0=200e9, b=0.01),
# Elastic torsion spring
ElasticUniaxial(id=3, name="GJ-spring", E=80e9 * 3.6e-4),
],
sections=[
# FiberSection for the column hinge region:
# - Rectangular concrete patch (core, excluding cover for simplicity)
# - Bottom rebar layer (3 × Ø20 → A=3×314e-6=942e-6 m²)
# - Top rebar layer
FiberSection(
id=1, name="RC-Column",
patches=[RectangularPatch(
material_id=1,
n_fib_y=8, n_fib_z=10,
y_i=-b / 2 + cover, z_i=-h / 2 + cover,
y_j=b / 2 - cover, z_j=h / 2 - cover,
)],
layers=[
# Bottom rebar (z = -h/2 + cover)
StraightLayer(
material_id=2, n_bars=3, bar_area=314e-6,
y_start=-b / 2 + cover, z_start=-h / 2 + cover,
y_end=b / 2 - cover, z_end=-h / 2 + cover,
),
# Top rebar (z = +h/2 - cover)
StraightLayer(
material_id=2, n_bars=3, bar_area=314e-6,
y_start=-b / 2 + cover, z_start=h / 2 - cover,
y_end=b / 2 - cover, z_end=h / 2 - cover,
),
],
),
# Aggregator: FiberSection + elastic torsion
SectionAggregator(
id=2, name="Col-Agg",
section_id=1,
pairings=[AggregatorDOF(material_id=3, dof="T")],
),
# Elastic beam section (for the beam and column elastic interior)
ElasticSection(
id=3, name="W14x90",
E=200e9, A=0.017,
Iz=4.16e-4, Iy=1.29e-4,
G=80e9, J=2.04e-6,
),
],
elements=[
# Columns: BeamWithHinges at the base (hinge at end-i only;
# end-j uses the same section but with a tiny Lp so it stays
# essentially elastic there).
BeamWithHingesElement(
id=1, name="Col-L", nodes=(1, 3),
section_i_id=2, section_j_id=2,
lp_i=0.40, lp_j=0.01, # plastic hinge at base only
E=200e9, A=0.017,
Iz=4.16e-4, Iy=1.29e-4,
G=80e9, J=2.04e-6,
),
BeamWithHingesElement(
id=2, name="Col-R", nodes=(2, 4),
section_i_id=2, section_j_id=2,
lp_i=0.40, lp_j=0.01,
E=200e9, A=0.017,
Iz=4.16e-4, Iy=1.29e-4,
G=80e9, J=2.04e-6,
),
# Beam: elastic
ElasticBeamColumn(id=3, name="Beam", nodes=(3, 4), section_id=3),
],
time_series=[LinearTimeSeries(id=1, name="Ramp")],
load_patterns=[
PlainLoadPattern(
id=1, name="PushRef",
time_series_id=1,
nodal_loads=[NodalLoad(node_id=3, forces=(1.0, 0, 0, 0, 0, 0))],
),
],
analyses=[
PushoverCase(
id=1, name="Push-X",
pattern_ids=[1],
control_node=3, control_dof=1,
target_disp=0.15, step_size=0.0005,
base_nodes=[1, 2],
),
ModalCase(id=2, name="Modal-3", n_modes=3),
],
)
def main() -> None:
project = build_portal_pushover()
project.validate_references()
print(f"Built '{project.meta.name}' — {len(project.nodes)} nodes, "
f"{len(project.elements)} elements, {len(project.analyses)} cases.")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,850 @@
{
"schema_version": 1,
"meta": {
"name": "RC Frame Earthquake (OpenSees Ex 3.3)",
"description": "Ex 3 gravity + uniform base excitation (horizontal, 4-s synthetic record peaking at ~0.35 g) + Rayleigh beta_k",
"author": "OpenSees Examples Manual",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [
{
"id": "X1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X2",
"ordinate": 360.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"y_grid_lines": [
{
"id": "Y1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y2",
"ordinate": 144.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"z_grid_lines": [
{
"id": "Z1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base-L",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "Base-R",
"coords": [
360.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 3,
"name": "Top-L",
"coords": [
0.0,
144.0,
0.0
],
"mass": [
0.46583850931677023,
0.46583850931677023,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "Top-R",
"coords": [
360.0,
144.0,
0.0
],
"mass": [
0.46583850931677023,
0.46583850931677023,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "Core-Conc",
"type": "Concrete01",
"fpc": -6.0,
"epsc0": -0.004,
"fpcu": -5.0,
"epsU": -0.014
},
{
"id": 2,
"name": "Cover-Conc",
"type": "Concrete01",
"fpc": -5.0,
"epsc0": -0.002,
"fpcu": 0.0,
"epsU": -0.006
},
{
"id": 3,
"name": "Steel-60",
"type": "Steel01",
"Fy": 60.0,
"E0": 30000.0,
"b": 0.01,
"a1": null,
"a2": null,
"a3": null,
"a4": null
}
],
"sections": [
{
"id": 1,
"name": "RC-Col",
"type": "FiberSection",
"GJ": null,
"patches": [
{
"kind": "rect",
"material_id": 1,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -10.5,
"z_i": -6.0,
"y_j": 10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": 6.0,
"y_j": 12.0,
"z_j": 7.5
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -7.5,
"y_j": 12.0,
"z_j": -6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -6.0,
"y_j": -10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": 10.5,
"z_i": -6.0,
"y_j": 12.0,
"z_j": 6.0
}
],
"layers": [
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": 10.5,
"z_start": 6.0,
"y_end": 10.5,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 2,
"bar_area": 0.6,
"y_start": 0.0,
"z_start": 6.0,
"y_end": 0.0,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": -10.5,
"z_start": 6.0,
"y_end": -10.5,
"z_end": -6.0
}
],
"fibres": []
},
{
"id": 2,
"name": "Beam",
"type": "ElasticSection",
"E": 4030.0,
"A": 360.0,
"Iz": 8640.0,
"Iy": 8640.0,
"G": 1500.0,
"J": 1.0
}
],
"elements": [
{
"id": 1,
"name": "Col-L",
"type": "ForceBeamColumn",
"nodes": [
1,
3
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
},
{
"id": 2,
"name": "Col-R",
"type": "ForceBeamColumn",
"nodes": [
2,
4
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
},
{
"id": 3,
"name": "Beam",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Gravity",
"type": "Constant",
"factor": 1.0
},
{
"id": 2,
"name": "GroundMotion",
"type": "Path",
"factor": 386.4,
"dt": 0.01,
"times": null,
"values": [
0.0,
0.0003759997006929128,
0.001492139322989129,
0.003313120974162101,
0.005781044089220584,
0.008816778784387096,
0.012321847906716393,
0.016180778098291576,
0.02026387021204836,
0.02443033041658253,
0.028531695488854605,
0.03241547927404673,
0.03592896222341777,
0.03892304240870259,
0.04125606453060492,
0.042797543233281915,
0.043431698518368936,
0.043060724200602765,
0.04160771510989262,
0.03901918503793526,
0.03526711513754839,
0.030350481468408082,
0.024296220477188758,
0.017159602214374983,
0.009023992816629927,
9.184850993605149e-18,
-0.009775992218015735,
-0.020143880860353257,
-0.03092246242551298,
-0.0419125696468492,
-0.05290067270632257,
-0.06366288085136805,
-0.07396927130647578,
-0.08358846462469952,
-0.09229235935153403,
-0.09986093421099113,
-0.10608702307869838,
-0.11078096685553815,
-0.11377504704082295,
-0.11492760833525657,
-0.11412678195541844,
-0.1112937274533204,
-0.10638531861325395,
-0.09939620831807686,
-0.09036021798258695,
-0.0793510090594839,
-0.06648200702603672,
-0.05190556192853958,
-0.035811343751739166,
-0.018423985333952782,
-3.6739403974420595e-17,
0.018799985034645625,
0.03730348307472823,
0.05521868290270173,
0.07226305111525734,
0.08816778784387104,
0.10268206588930338,
0.11557698641636838,
0.1266491888253022,
0.1357240578699029,
0.142658477444273,
0.14734308760930329,
0.14970400926424074,
0.14970400926424074,
0.14734308760930329,
0.14265847744427304,
0.13572405786990288,
0.12664918882530227,
0.11557698641636828,
0.10268206588930326,
0.088167787843871,
0.07226305111525742,
0.055218682902701696,
0.03730348307472832,
0.018799985034645583,
5.510910596163089e-17,
-0.018799985034645476,
-0.037303483074728215,
-0.055218682902701585,
-0.07226305111525733,
-0.08816778784387092,
-0.10268206588930337,
-0.11557698641636836,
-0.12664918882530235,
-0.13572405786990283,
-0.142658477444273,
-0.14734308760930326,
-0.14970400926424074,
-0.14970400926424074,
-0.14734308760930329,
-0.14265847744427307,
-0.13572405786990288,
-0.12664918882530227,
-0.11557698641636828,
-0.10268206588930327,
-0.08816778784387101,
-0.07226305111525745,
-0.05521868290270171,
-0.03730348307472834,
-0.0187999850346456,
-7.347880794884119e-17,
0.018799985034645455,
0.037303483074728194,
0.05521868290270157,
0.07226305111525731,
0.0881677878438709,
0.10268206588930336,
0.11557698641636836,
0.12664918882530232,
0.13572405786990294,
0.1426584774442731,
0.1473430876093033,
0.14970400926424074,
0.14970400926424074,
0.14734308760930329,
0.14265847744427299,
0.13572405786990302,
0.12664918882530243,
0.11557698641636846,
0.10268206588930347,
0.08816778784387104,
0.07226305111525747,
0.05521868290270173,
0.037303483074728354,
0.01879998503464562,
9.18485099360515e-17,
-0.018799985034645438,
-0.03730348307472817,
-0.0552186829027018,
-0.0722630511152573,
-0.08816778784387089,
-0.10268206588930313,
-0.11557698641636852,
-0.12664918882530232,
-0.13572405786990294,
-0.142658477444273,
-0.14734308760930334,
-0.14970400926424074,
-0.14970400926424074,
-0.1473430876093033,
-0.14265847744427307,
-0.13572405786990302,
-0.12664918882530243,
-0.11557698641636865,
-0.10268206588930329,
-0.08816778784387105,
-0.07226305111525748,
-0.055218682902701995,
-0.03730348307472811,
-0.01879998503464564,
-1.1021821192326178e-16,
0.01879998503464542,
0.0373034830747279,
0.055218682902701786,
0.07226305111525728,
0.08816778784387087,
0.10268206588930313,
0.11557698641636852,
0.1266491888253023,
0.1357240578699029,
0.14265847744427299,
0.14734308760930334,
0.14970400926424074,
0.14970400926424074,
0.1473430876093033,
0.14265847744427307,
0.1357240578699028,
0.12664918882530243,
0.11557698641636867,
0.1026820658893033,
0.08816778784387107,
0.07226305111525749,
0.05521868290270201,
0.03730348307472813,
0.01879998503464566,
1.2858791391047207e-16,
-0.0187999850346454,
-0.03730348307472788,
-0.05521868290270177,
-0.07226305111525726,
-0.08816778784387087,
-0.10268206588930312,
-0.1155769864163685,
-0.1266491888253023,
-0.1357240578699029,
-0.14265847744427299,
-0.14734308760930334,
-0.14970400926424074,
-0.14970400926424074,
-0.1473430876093033,
-0.14265847744427307,
-0.1357240578699028,
-0.12664918882530243,
-0.11557698641636868,
-0.10268206588930333,
-0.08816778784387108,
-0.0722630511152575,
-0.05521868290270203,
-0.03730348307472815,
-0.018799985034645677,
-1.4695761589768238e-16,
0.01856644786983349,
0.03638245679596323,
0.0531863271257785,
0.06873874052503183,
0.08282597161989906,
0.09526261779156023,
0.10589381608321365,
0.11459692501303523,
0.1212826580513477,
0.12589566447200526,
0.1284145620861484,
0.12885143487654274,
0.12725081664585672,
0.12368818935544204,
0.11826803175658426,
0.1111214601106251,
0.10240350817844192,
0.0922900981688023,
0.08097475791981867,
0.06866514221447995,
0.055579417782458194,
0.04194257221409126,
0.027982706722276016,
0.013927371462208751,
1.2095604965641715e-16,
-0.013583503435593256,
-0.026617974011402645,
-0.038911948171488256,
-0.05029033688222117,
-0.06059677531980971,
-0.06969562003067588,
-0.07747357085528037,
-0.08384090136876289,
-0.08873228815055724,
-0.0921072357443415,
-0.09395010060656506,
-0.09426972356787908,
-0.09309868625433226,
-0.09049221244855306,
-0.08652674043787481,
-0.08129819692830416,
-0.07492000704231337,
-0.06752087821731563,
-0.05924239844431021,
-0.05023649120795737,
-0.04066277069740133,
-0.030685841350052333,
-0.020472585578247403,
-0.010189482631897374,
-9.832592927142318e-17,
0.009937903409330418,
0.019474125797637704,
0.0284685894350225,
0.03679319644802473,
0.044333547887740916,
0.05099040488356929,
0.056680875268027994,
0.06133931378636083,
0.06491792880314469,
0.06738709320963301,
0.06873536194487236,
0.06896920309873988,
0.06811245389806937,
0.06620551692532123,
0.06330431562615867,
0.05947903147792919,
0.05481264807294881,
0.04939932978399098,
0.043342664598141574,
0.03675380211118236,
0.029749518558492356,
0.02245024111907521,
0.014978063573993635,
0.007454784744376489,
2.878198652327287e-16,
-0.0072707254532282444,
-0.014247574793624922,
-0.0208280649647277,
-0.02691847756027895,
-0.03243511652132547,
-0.03730537713913632,
-0.04146861420846457,
-0.04487680063496369,
-0.04749497131122594,
-0.049301450581451696,
-0.05028786506017283,
-0.05045894690303327,
-0.049832135797735155,
-0.04843699090505647,
-0.04631442669186875,
-0.04351578902386234,
-0.04010178999412147,
-0.03614132172945704,
-0.03171017082019282,
-0.02688965604775296,
-0.021765212731031044,
-0.016424947276336123,
-0.010958185402089994,
-0.005454037029418993,
-6.3156092834735e-17,
0.005319376375362082,
0.01042374839360207,
0.015238137848910902,
0.01969397889054942,
0.023730038173707255,
0.027293196958740988,
0.03033908626567152,
0.03283256872166209,
0.034748063307665326,
0.03606971177513051,
0.03679138802438222,
0.03691655417444788,
0.03645796937341448,
0.035437259565285636,
0.03388435841347474,
0.03183683135580237,
0.029339096308435262,
0.026441555828059723,
0.023199656568620733,
0.01967289262153575,
0.015923769801370835,
0.01201674813199198,
0.008017179705064093,
0.003990258731576542,
-6.164690305841934e-17,
-0.003891738892464325,
-0.007626177236966827,
-0.011148459806306474,
-0.014408422752471289,
-0.017361266803384564,
-0.01996812945893378,
-0.022196549679925754,
-0.024020820415232586,
-0.025422226191515247,
-0.026389164866286233,
-0.026917154489278325,
-0.027008728000881468,
-0.02667322019324803,
-0.025926452945001868,
-0.02479032619212772,
-0.02329232339604356,
-0.021464941398422012,
-0.0193450554974736,
-0.016973231331797612,
-0.014392995708504558,
-0.011650078858431393,
-0.008791640742480778,
-0.005865493972298641,
-0.002919335651562286,
-3.943919054879792e-17,
0.0028472569975064722,
0.0055794304556819685,
0.008156387432977339,
0.010541427274178315,
0.012701774131668711,
0.014608995592985858,
0.016239342654524213,
0.01757400763590476,
0.01859929800432925,
0.019306725451024603,
0.019693010911127292,
0.01976000752341367,
0.019514544767715435,
0.018968198177713044,
0.018136990088845957,
0.01704102783102496,
0.015704086601509296,
0.014153144944791883,
0.012417881316069912,
0.010530140607703934,
0.008523379771289316,
0.00643210177128294,
0.004291287061626068,
0.0021358315888240385,
9.070586579183172e-17,
-0.0020830977190033637,
-0.004081998521997866,
-0.005967340521710147,
-0.007712272945193301,
-0.009292816470078945,
-0.010688169499041026,
-0.011880956889869792,
-0.012857418649655518,
-0.013607537107403339,
-0.014125102083751515,
-0.014407714563597466,
-0.01445673033223267,
-0.014277145943839522,
-0.013877430239772084,
-0.01326930541105003,
-0.012467482294492575,
-0.01148935519599578,
-0.010354662040356868,
-0.009085116049241277,
-0.00770401544360185,
-0.006235837852122879,
-0.004705826182846021,
-0.003139572682582101,
-0.0015626077711816495
],
"file_path": null
}
],
"load_patterns": [
{
"id": 1,
"name": "Gravity",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 3,
"forces": [
0.0,
-180.0,
0.0,
0.0,
0.0,
0.0
]
},
{
"node_id": 4,
"forces": [
0.0,
-180.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
},
{
"id": 2,
"name": "GroundMotion",
"type": "UniformExcitation",
"direction": 1,
"accel_series_id": 2,
"vel_series_id": null,
"disp_series_id": null,
"factor": 1.0
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Earthquake",
"type": "Transient",
"pattern_ids": [
1,
2
],
"dt": 0.01,
"n_steps": 400,
"system": "BandGeneral",
"constraints": "Plain",
"integrator": "Newmark",
"integrator_params": [
0.5,
0.25
],
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-12,
"max_iter": 10,
"rayleigh_alpha_m": 0.0,
"rayleigh_beta_k": 0.000625
}
]
}

View file

@ -0,0 +1,161 @@
"""RC Frame Earthquake Analysis — OpenSees Examples Manual, Example 3.3.
Time-history analysis of the RC portal frame under horizontal ground
motion. Sources the Example 3 gravity model, adds lumped joint
masses, a PathTimeSeries from a ground-motion record, a
UniformExcitation load pattern in +X, and stiffness-proportional
Rayleigh damping. Newmark integrator with average-acceleration
(gamma = 0.5, beta = 0.25).
Matches the Tcl walkthrough at:
https://opensees.berkeley.edu/wiki/index.php?title=RC_Portal_Frame_Earthquake_Analysis
Model (kip-in-ksi):
- Geometry + section + elements = Example 3 (rc_frame_gravity).
- Gravity pattern uses ConstantTimeSeries so it stays locked during
the transient (equivalent to ``loadConst -time 0.0``).
- Nodal masses: m = P/g = 180/386.4 kip·s^2/in at nodes 3 and 4.
- Ground-motion record: since the Tcl ships ARL360.at2 from the
PEER strong-motion database (not redistributable without
attribution), we bundle a short synthetic acceleration record
that reproduces the same classroom behaviour: a ~4-second
pulse-like time history with peak amplitude ≈ 0.35 g.
- UniformExcitation pattern in DOF 1 (+X), scale factor = g
(so the path data is in "g" units, multiplied to in/s²).
- Rayleigh damping: alpha_m = 0, beta_kcommit = 0.000625.
GUI walkthrough: File → Open → rc_frame_earthquake.osmodel → Analyze
→ Run → Earthquake → Display → Show Time-History Plot (Node 3 Ux).
"""
from __future__ import annotations
import math
from pathlib import Path
from otko.core import (
ConstantTimeSeries,
NodalLoad,
PathTimeSeries,
PlainLoadPattern,
TransientCase,
UniformExcitationPattern,
)
from otko.services import load_project, save_project
try:
from examples.rc_frame_gravity import build_rc_frame_gravity, P_LOAD
except ImportError:
import sys
sys.path.insert(0, str(Path(__file__).parent))
from rc_frame_gravity import build_rc_frame_gravity, P_LOAD # type: ignore
G = 386.4 # in/s² (gravity)
DT = 0.01 # s — time step of bundled ground motion
N_PTS = 400 # 4-second duration
BETA_K_COMMIT = 0.000625 # Tcl reference stiffness-damping coeff
def _synthetic_ground_motion() -> list[float]:
"""Bundle a short acceleration signal (units of g).
Decaying sinusoid centred at ~2 Hz with an exponential envelope —
peak ~0.15 g, enough to drive the fibre section into inelastic
cycles without blowing past its crushing strain on the very first
impulse (which would require a much tighter Newmark step).
"""
out: list[float] = []
peak = 0.15 # units of g
freq = 2.0 # Hz (period ~0.5 s)
for i in range(N_PTS):
t = i * DT
if t < 0.5:
env = t / 0.5
elif t < 2.0:
env = 1.0
else:
env = math.exp(-(t - 2.0) / 0.8)
out.append(peak * env * math.sin(2.0 * math.pi * freq * t))
return out
def build_rc_frame_earthquake(): # type: ignore[no-untyped-def]
"""Ex 3 gravity + lumped masses + ground motion + Rayleigh damping."""
proj = build_rc_frame_gravity()
proj.meta.name = "RC Frame Earthquake (OpenSees Ex 3.3)"
proj.meta.description = (
"Ex 3 gravity + uniform base excitation (horizontal, 4-s "
"synthetic record peaking at ~0.35 g) + Rayleigh beta_k"
)
# Locked-in gravity — Constant TS, matches the Tcl loadConst.
proj.time_series = [
ConstantTimeSeries(id=1, name="Gravity"),
PathTimeSeries(
id=2, name="GroundMotion",
dt=DT, factor=G,
values=_synthetic_ground_motion(),
),
]
# Lumped mass m = P/g at each top node (gravity is the sole
# tributary weight; m_x = m_y because a point mass is isotropic).
m = P_LOAD / G # ≈ 0.466 kip·s²/in
for n in proj.nodes:
if n.id in (3, 4):
n.mass = (m, m, 0.0, 0.0, 0.0, 0.0)
# Gravity pattern (now with Constant TS).
proj.load_patterns = [
PlainLoadPattern(
id=1, name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=3, forces=(0, -P_LOAD, 0, 0, 0, 0)),
NodalLoad(node_id=4, forces=(0, -P_LOAD, 0, 0, 0, 0)),
],
),
# Ground motion — applied as UniformExcitation in +X (dir=1).
UniformExcitationPattern(
id=2, name="GroundMotion",
direction=1,
accel_series_id=2,
),
]
proj.analyses = [TransientCase(
id=1, name="Earthquake",
pattern_ids=[1, 2],
dt=DT,
n_steps=N_PTS,
system="BandGeneral", constraints="Plain",
integrator="Newmark",
integrator_params=(0.5, 0.25), # average-acceleration method
algorithm="Newton",
test="NormDispIncr", tolerance=1e-12, max_iter=10,
rayleigh_alpha_m=0.0,
rayleigh_beta_k=BETA_K_COMMIT,
)]
return proj
def main() -> None:
project = build_rc_frame_earthquake()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" Ground motion: {N_PTS} points, dt = {DT} s, "
f"total = {N_PTS * DT:.2f} s")
print(f" Nodal mass (3, 4): {P_LOAD / G:.4f} kip*s^2/in")
print(f" Rayleigh beta_k = {BETA_K_COMMIT}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,422 @@
{
"schema_version": 1,
"meta": {
"name": "RC Frame Gravity (OpenSees Ex 3)",
"description": "1-bay 1-storey portal frame, nonlinear fibre columns + elastic beam, 10-step LoadControl gravity pushdown.",
"author": "OpenSees Examples Manual",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [
{
"id": "X1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X2",
"ordinate": 360.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"y_grid_lines": [
{
"id": "Y1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y2",
"ordinate": 144.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"z_grid_lines": [
{
"id": "Z1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base-L",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "Base-R",
"coords": [
360.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 3,
"name": "Top-L",
"coords": [
0.0,
144.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "Top-R",
"coords": [
360.0,
144.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "Core-Conc",
"type": "Concrete01",
"fpc": -6.0,
"epsc0": -0.004,
"fpcu": -5.0,
"epsU": -0.014
},
{
"id": 2,
"name": "Cover-Conc",
"type": "Concrete01",
"fpc": -5.0,
"epsc0": -0.002,
"fpcu": 0.0,
"epsU": -0.006
},
{
"id": 3,
"name": "Steel-60",
"type": "Steel01",
"Fy": 60.0,
"E0": 30000.0,
"b": 0.01,
"a1": null,
"a2": null,
"a3": null,
"a4": null
}
],
"sections": [
{
"id": 1,
"name": "RC-Col",
"type": "FiberSection",
"GJ": null,
"patches": [
{
"kind": "rect",
"material_id": 1,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -10.5,
"z_i": -6.0,
"y_j": 10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": 6.0,
"y_j": 12.0,
"z_j": 7.5
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -7.5,
"y_j": 12.0,
"z_j": -6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -6.0,
"y_j": -10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": 10.5,
"z_i": -6.0,
"y_j": 12.0,
"z_j": 6.0
}
],
"layers": [
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": 10.5,
"z_start": 6.0,
"y_end": 10.5,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 2,
"bar_area": 0.6,
"y_start": 0.0,
"z_start": 6.0,
"y_end": 0.0,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": -10.5,
"z_start": 6.0,
"y_end": -10.5,
"z_end": -6.0
}
],
"fibres": []
},
{
"id": 2,
"name": "Beam",
"type": "ElasticSection",
"E": 4030.0,
"A": 360.0,
"Iz": 8640.0,
"Iy": 8640.0,
"G": 1500.0,
"J": 1.0
}
],
"elements": [
{
"id": 1,
"name": "Col-L",
"type": "ForceBeamColumn",
"nodes": [
1,
3
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
},
{
"id": 2,
"name": "Col-R",
"type": "ForceBeamColumn",
"nodes": [
2,
4
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
},
{
"id": 3,
"name": "Beam",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Gravity",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "Gravity",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 3,
"forces": [
0.0,
-180.0,
0.0,
0.0,
0.0,
0.0
]
},
{
"node_id": 4,
"forces": [
0.0,
-180.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Gravity",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 10,
"load_factor_increment": 0.1,
"system": "BandGeneral",
"constraints": "Transformation",
"integrator": "LoadControl",
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-12,
"max_iter": 10
}
]
}

View file

@ -0,0 +1,232 @@
"""RC Frame Gravity Analysis — OpenSees Examples Manual, Example 3.
Single-bay, single-storey RC portal frame under gravity (two 180-kip
nodal loads on top joints). Columns are nonlinear forceBeamColumn
elements with the fibre section from the Moment-Curvature example;
the beam is an elasticBeamColumn with stiffness (A, E, Iz) =
(360, 4030, 8640) — matching the Tcl reference at:
https://opensees.berkeley.edu/wiki/index.php?title=RC_Portal_Frame
Model (kip-in-ksi):
node 3 ─────── elasticBeam 3 ─────── node 4
│ │
forceBC 1 forceBC 2
│ │
node 1 node 2
(fixed) (fixed)
Width 360", height 144", columns fibre-section RC (Concrete01 +
Steel01). Load pattern: 180 kip ↓ at each top node, Linear time series,
LoadControl 0.1 × 10 steps = full gravity.
Expected terminal state (nodes 3 & 4): Uy ≈ -0.0203 in, Ux ≈ 0,
column axial force ≈ 180 kip compression.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
Concrete01,
CoordinateGridSystem,
ElasticBeamColumn,
ElasticSection,
FiberSection,
ForceBeamColumn,
GridSystem,
LinearTimeSeries,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
RectangularPatch,
StaticCase,
Steel01,
StraightLayer,
UnitSystem,
make_grid_lines,
)
from otko.services import load_project, save_project
# Frame geometry (inches).
WIDTH = 360.0
HEIGHT = 144.0
# Column section parameters (same as Moment-Curvature example).
COL_WIDTH = 15.0
COL_DEPTH = 24.0
COVER = 1.5
AS_BAR = 0.60
# Material properties (kip, in, ksi).
CONC_CORE_FPC = -6.0
CONC_COVER_FPC = -5.0
STEEL_FY = 60.0
# Steel Young's modulus: 30000 ksi (matches OpenSees Wiki, which sources
# the MK example's $E = 30000). Some Tcl reprints show 3000 — that's a
# typo; 3000 gives Uy ≈ -0.0203 instead of the reference -0.01837.
STEEL_E = 30000.0
STEEL_B = 0.01
# Beam elastic properties.
BEAM_A = 360.0
BEAM_E = 4030.0
BEAM_IZ = 8640.0
# Gravity load.
P_LOAD = 180.0 # kip, pointing -Y (compression on columns)
def build_rc_frame_gravity() -> Project:
y1 = COL_DEPTH / 2.0 # 12
z1 = COL_WIDTH / 2.0 # 7.5
return Project(
meta=ProjectMeta(
name="RC Frame Gravity (OpenSees Ex 3)",
author="OpenSees Examples Manual",
description=(
"1-bay 1-storey portal frame, nonlinear fibre columns + "
"elastic beam, 10-step LoadControl gravity pushdown."
),
units=UnitSystem.US_IN_KIP,
),
ndm=2, ndf=3,
coord_systems=[
CoordinateGridSystem(
name="Global",
grid=GridSystem(
x_grid_lines=make_grid_lines("X", [0.0, WIDTH]),
y_grid_lines=make_grid_lines("Y", [0.0, HEIGHT]),
z_grid_lines=make_grid_lines("Z", [0.0]),
),
),
],
nodes=[
Node(id=1, name="Base-L",
coords=(0.0, 0.0, 0.0),
restraint=(True, True, False, False, False, True)),
Node(id=2, name="Base-R",
coords=(WIDTH, 0.0, 0.0),
restraint=(True, True, False, False, False, True)),
Node(id=3, name="Top-L", coords=(0.0, HEIGHT, 0.0)),
Node(id=4, name="Top-R", coords=(WIDTH, HEIGHT, 0.0)),
],
materials=[
Concrete01(id=1, name="Core-Conc",
fpc=CONC_CORE_FPC, epsc0=-0.004,
fpcu=-5.0, epsU=-0.014),
Concrete01(id=2, name="Cover-Conc",
fpc=CONC_COVER_FPC, epsc0=-0.002,
fpcu=0.0, epsU=-0.006),
Steel01(id=3, name="Steel-60",
Fy=STEEL_FY, E0=STEEL_E, b=STEEL_B),
],
sections=[
# Fibre section for the columns (MK recipe).
FiberSection(
id=1, name="RC-Col",
patches=[
RectangularPatch(
material_id=1, n_fib_y=10, n_fib_z=1,
y_i=COVER - y1, z_i=COVER - z1,
y_j=y1 - COVER, z_j=z1 - COVER,
),
RectangularPatch(
material_id=2, n_fib_y=10, n_fib_z=1,
y_i=-y1, z_i=z1 - COVER,
y_j=y1, z_j=z1,
),
RectangularPatch(
material_id=2, n_fib_y=10, n_fib_z=1,
y_i=-y1, z_i=-z1,
y_j=y1, z_j=COVER - z1,
),
RectangularPatch(
material_id=2, n_fib_y=2, n_fib_z=1,
y_i=-y1, z_i=COVER - z1,
y_j=COVER - y1, z_j=z1 - COVER,
),
RectangularPatch(
material_id=2, n_fib_y=2, n_fib_z=1,
y_i=y1 - COVER, z_i=COVER - z1,
y_j=y1, z_j=z1 - COVER,
),
],
layers=[
StraightLayer(
material_id=3, n_bars=3, bar_area=AS_BAR,
y_start=y1 - COVER, z_start=z1 - COVER,
y_end=y1 - COVER, z_end=COVER - z1,
),
StraightLayer(
material_id=3, n_bars=2, bar_area=AS_BAR,
y_start=0.0, z_start=z1 - COVER,
y_end=0.0, z_end=COVER - z1,
),
StraightLayer(
material_id=3, n_bars=3, bar_area=AS_BAR,
y_start=COVER - y1, z_start=z1 - COVER,
y_end=COVER - y1, z_end=COVER - z1,
),
],
),
# Elastic section for the beam.
ElasticSection(
id=2, name="Beam",
E=BEAM_E, A=BEAM_A, Iz=BEAM_IZ,
Iy=BEAM_IZ, G=1500.0, J=1.0, # placeholders for 3D round-trip
),
],
elements=[
# Columns — fibre-section forceBeamColumn.
ForceBeamColumn(id=1, name="Col-L",
nodes=(1, 3), section_id=1,
integration_points=5, geom_transf="Linear"),
ForceBeamColumn(id=2, name="Col-R",
nodes=(2, 4), section_id=1,
integration_points=5, geom_transf="Linear"),
# Beam — elastic.
ElasticBeamColumn(id=3, name="Beam",
nodes=(3, 4), section_id=2,
geom_transf="Linear"),
],
time_series=[LinearTimeSeries(id=1, name="Gravity")],
load_patterns=[PlainLoadPattern(
id=1, name="Gravity", time_series_id=1,
nodal_loads=[
NodalLoad(node_id=3, forces=(0, -P_LOAD, 0, 0, 0, 0)),
NodalLoad(node_id=4, forces=(0, -P_LOAD, 0, 0, 0, 0)),
],
)],
analyses=[StaticCase(
id=1, name="Gravity",
pattern_ids=[1],
n_steps=10, load_factor_increment=0.1,
system="BandGeneral", constraints="Transformation",
integrator="LoadControl", algorithm="Newton",
test="NormDispIncr", tolerance=1e-12, max_iter=10,
)],
)
def main() -> None:
project = build_rc_frame_gravity()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" ndm={project.ndm}, ndf={project.ndf}, units={project.meta.units.value}")
print(f" Frame: {WIDTH}in x {HEIGHT}in, P = -{P_LOAD} kip at each top node")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,484 @@
{
"schema_version": 1,
"meta": {
"name": "RC Frame Pushover (OpenSees Ex 3.2)",
"description": "Ex 3 gravity preload (StaticCase) + lateral reference load + DisplacementControl pushover on node 3 (DOF 1) to 15 in, chained via preload_case_ids.",
"author": "OpenSees Examples Manual",
"units": "US (in, kip, kip·s²/in, s, ksi)"
},
"ndm": 2,
"ndf": 3,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [
{
"id": "X1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "X2",
"ordinate": 360.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"y_grid_lines": [
{
"id": "Y1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
},
{
"id": "Y2",
"ordinate": 144.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"z_grid_lines": [
{
"id": "Z1",
"ordinate": 0.0,
"line_type": "Primary",
"visible": true,
"bubble_loc": "End",
"color": "#808080"
}
],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base-L",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 2,
"name": "Base-R",
"coords": [
360.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
false,
false,
false,
true
]
},
{
"id": 3,
"name": "Top-L",
"coords": [
0.0,
144.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
},
{
"id": 4,
"name": "Top-R",
"coords": [
360.0,
144.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "Core-Conc",
"type": "Concrete01",
"fpc": -6.0,
"epsc0": -0.004,
"fpcu": -5.0,
"epsU": -0.014
},
{
"id": 2,
"name": "Cover-Conc",
"type": "Concrete01",
"fpc": -5.0,
"epsc0": -0.002,
"fpcu": 0.0,
"epsU": -0.006
},
{
"id": 3,
"name": "Steel-60",
"type": "Steel01",
"Fy": 60.0,
"E0": 30000.0,
"b": 0.01,
"a1": null,
"a2": null,
"a3": null,
"a4": null
}
],
"sections": [
{
"id": 1,
"name": "RC-Col",
"type": "FiberSection",
"GJ": null,
"patches": [
{
"kind": "rect",
"material_id": 1,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -10.5,
"z_i": -6.0,
"y_j": 10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": 6.0,
"y_j": 12.0,
"z_j": 7.5
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 10,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -7.5,
"y_j": 12.0,
"z_j": -6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": -12.0,
"z_i": -6.0,
"y_j": -10.5,
"z_j": 6.0
},
{
"kind": "rect",
"material_id": 2,
"n_fib_y": 2,
"n_fib_z": 1,
"y_i": 10.5,
"z_i": -6.0,
"y_j": 12.0,
"z_j": 6.0
}
],
"layers": [
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": 10.5,
"z_start": 6.0,
"y_end": 10.5,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 2,
"bar_area": 0.6,
"y_start": 0.0,
"z_start": 6.0,
"y_end": 0.0,
"z_end": -6.0
},
{
"kind": "straight",
"material_id": 3,
"n_bars": 3,
"bar_area": 0.6,
"y_start": -10.5,
"z_start": 6.0,
"y_end": -10.5,
"z_end": -6.0
}
],
"fibres": []
},
{
"id": 2,
"name": "Beam",
"type": "ElasticSection",
"E": 4030.0,
"A": 360.0,
"Iz": 8640.0,
"Iy": 8640.0,
"G": 1500.0,
"J": 1.0
}
],
"elements": [
{
"id": 1,
"name": "Col-L",
"type": "ForceBeamColumn",
"nodes": [
1,
3
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
},
{
"id": 2,
"name": "Col-R",
"type": "ForceBeamColumn",
"nodes": [
2,
4
],
"section_id": 1,
"integration_points": 5,
"geom_transf": "Linear",
"max_iter": 10,
"tolerance": 1e-12
},
{
"id": 3,
"name": "Beam",
"type": "ElasticBeamColumn",
"nodes": [
3,
4
],
"section_id": 2,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Gravity",
"type": "Linear",
"factor": 1.0
},
{
"id": 2,
"name": "Lateral",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "Gravity",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 3,
"forces": [
0.0,
-180.0,
0.0,
0.0,
0.0,
0.0
]
},
{
"node_id": 4,
"forces": [
0.0,
-180.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
},
{
"id": 2,
"name": "Lateral",
"type": "Plain",
"time_series_id": 2,
"nodal_loads": [
{
"node_id": 3,
"forces": [
10.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
},
{
"node_id": 4,
"forces": [
10.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 100,
"name": "Gravity-Preload",
"type": "Static",
"pattern_ids": [
1
],
"n_steps": 10,
"load_factor_increment": 0.1,
"system": "BandGeneral",
"constraints": "Transformation",
"integrator": "LoadControl",
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-12,
"max_iter": 10
},
{
"id": 1,
"name": "Pushover",
"type": "Pushover",
"pattern_ids": [
2
],
"control_node": 3,
"control_dof": 1,
"target_disp": 15.0,
"step_size": 0.1,
"base_nodes": [
1,
2
],
"system": "BandGeneral",
"constraints": "Transformation",
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-12,
"max_iter": 10,
"preload_case_ids": [
100
]
}
]
}

View file

@ -0,0 +1,139 @@
"""RC Frame Pushover Analysis — OpenSees Examples Manual, Example 3.2.
Sources the Example 3 gravity model and extends it with a lateral
reference load pattern + DisplacementControl pushover on the top-left
joint (node 3, DOF 1 = Ux). Target displacement 15 in with dU = 0.1
in per step. Matches the Tcl walkthrough at:
https://opensees.berkeley.edu/wiki/index.php?title=RC_Portal_Frame_Pushover_Analysis
Model (kip-in-ksi):
- Geometry + section + elements = Example 3 (rc_frame_gravity).
- Gravity preload = a ``StaticCase`` with 10 × LoadControl(0.1)
steps under a Linear TS — same recipe the Tcl uses
(``analyze 10; loadConst -time 0.0``). Referenced by the
PushoverCase via ``preload_case_ids``.
- Lateral pattern: H = 10 kip at nodes 3 and 4 in +X, Linear TS,
scaled by DisplacementControl.
GUI walkthrough: File → Open → rc_frame_pushover.osmodel → Analyze →
Run → Pushover → Display → Show Pushover Curve.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
LinearTimeSeries,
NodalLoad,
PlainLoadPattern,
PushoverCase,
StaticCase,
)
from otko.services import load_project, save_project
# Reuse the Example 3 gravity project as the foundation — identical
# geometry, materials, sections, elements. Only the load patterns and
# analysis case change for the pushover. Works both when importing as
# ``examples.rc_frame_pushover`` (pytest) and when running this file
# directly (``python examples/rc_frame_pushover.py``).
try:
from examples.rc_frame_gravity import build_rc_frame_gravity, P_LOAD
except ImportError:
import sys
sys.path.insert(0, str(Path(__file__).parent))
from rc_frame_gravity import build_rc_frame_gravity, P_LOAD # type: ignore
# Pushover parameters from the Tcl reference.
H_LATERAL = 10.0 # kip — reference lateral load
D_STEP = 0.1 # in — DisplacementControl increment
D_TARGET = 15.0 # in — total pushover displacement
def build_rc_frame_pushover(): # type: ignore[no-untyped-def]
"""Start from the Ex3 gravity model and re-plumb for pushover.
Three edits to the gravity project:
1. Add a ``LinearTimeSeries`` + ``PlainLoadPattern`` for the
lateral reference load (H = 10 kip at nodes 3 & 4, +X).
2. Keep the gravity pattern on its own Linear TS — the preload
runs as a 10-step ``LoadControl(0.1)`` ramp, exactly as the
Tcl walkthrough does.
3. Replace the StaticCase with two cases: a preload ``StaticCase``
(id 100) for gravity, and a ``PushoverCase`` whose
``preload_case_ids=[100]`` references it. The pushover's own
``pattern_ids`` holds only the lateral reference.
"""
proj = build_rc_frame_gravity()
proj.meta.name = "RC Frame Pushover (OpenSees Ex 3.2)"
proj.meta.description = (
"Ex 3 gravity preload (StaticCase) + lateral reference load + "
"DisplacementControl pushover on node 3 (DOF 1) to 15 in, "
"chained via preload_case_ids."
)
proj.time_series = [
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
]
proj.load_patterns = [
PlainLoadPattern(
id=1, name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=3, forces=(0, -P_LOAD, 0, 0, 0, 0)),
NodalLoad(node_id=4, forces=(0, -P_LOAD, 0, 0, 0, 0)),
],
),
# Lateral reference — scaled by the DisplacementControl factor.
PlainLoadPattern(
id=2, name="Lateral",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=3, forces=(H_LATERAL, 0, 0, 0, 0, 0)),
NodalLoad(node_id=4, forces=(H_LATERAL, 0, 0, 0, 0, 0)),
],
),
]
proj.analyses = [
StaticCase(
id=100, name="Gravity-Preload",
pattern_ids=[1],
n_steps=10, load_factor_increment=0.1,
system="BandGeneral", constraints="Transformation",
integrator="LoadControl", algorithm="Newton",
test="NormDispIncr", tolerance=1e-12, max_iter=10,
),
PushoverCase(
id=1, name="Pushover",
preload_case_ids=[100],
pattern_ids=[2], # lateral only
control_node=3, control_dof=1, # Ux at top-left joint
target_disp=D_TARGET,
step_size=D_STEP,
base_nodes=[1, 2],
system="BandGeneral", constraints="Transformation",
algorithm="Newton",
test="NormDispIncr", tolerance=1e-12, max_iter=10,
),
]
return proj
def main() -> None:
project = build_rc_frame_pushover()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" H = {H_LATERAL} kip reference, dU = {D_STEP} in, target = {D_TARGET} in")
print(f" Steps = {int(D_TARGET / D_STEP)}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,202 @@
{
"schema_version": 1,
"meta": {
"name": "SDOF Pushover",
"description": "",
"author": "Ozan",
"units": "SI (m, N, kg, s, Pa)"
},
"ndm": 3,
"ndf": 6,
"coord_systems": [
{
"name": "Global",
"coord": {
"origin": [
0.0,
0.0,
0.0
],
"rotation_deg": [
0.0,
0.0,
0.0
]
},
"grid": {
"x_grid_lines": [],
"y_grid_lines": [],
"z_grid_lines": [],
"visible": true,
"is_general": false,
"hide_all": false,
"glue_to_grid": false,
"bubble_size": 20
}
}
],
"nodes": [
{
"id": 1,
"name": "Base",
"coords": [
0.0,
0.0,
0.0
],
"mass": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"restraint": [
true,
true,
true,
true,
true,
true
]
},
{
"id": 2,
"name": "Top",
"coords": [
0.0,
0.0,
3.0
],
"mass": [
5000.0,
5000.0,
5000.0,
0.0,
0.0,
0.0
],
"restraint": [
false,
false,
false,
false,
false,
false
]
}
],
"materials": [
{
"id": 1,
"name": "HingeSteel",
"type": "Hysteretic",
"s1p": 50000.0,
"e1p": 0.002,
"s2p": 150000.0,
"e2p": 0.01,
"s3p": 165000.0,
"e3p": 0.05,
"s1n": -50000.0,
"e1n": -0.002,
"s2n": -150000.0,
"e2n": -0.01,
"s3n": -165000.0,
"e3n": -0.05,
"px": 1.0,
"py": 1.0,
"d1": 0.0,
"d2": 0.0,
"beta": 0.0
}
],
"sections": [
{
"id": 1,
"name": "W12x40",
"type": "ElasticSection",
"E": 200000000000.0,
"A": 0.0076,
"Iz": 0.0002,
"Iy": 4.5e-05,
"G": 80000000000.0,
"J": 8.5e-07
}
],
"elements": [
{
"id": 1,
"name": "Col",
"type": "ElasticBeamColumn",
"nodes": [
1,
2
],
"section_id": 1,
"geom_transf": "Linear",
"rho": 0.0
}
],
"time_series": [
{
"id": 1,
"name": "Ramp",
"type": "Linear",
"factor": 1.0
}
],
"load_patterns": [
{
"id": 1,
"name": "PushRef",
"type": "Plain",
"time_series_id": 1,
"nodal_loads": [
{
"node_id": 2,
"forces": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0
]
}
],
"element_loads": []
}
],
"spectra": [],
"analyses": [
{
"id": 1,
"name": "Push-X",
"type": "Pushover",
"pattern_ids": [
1
],
"control_node": 2,
"control_dof": 1,
"target_disp": 0.1,
"step_size": 0.001,
"base_nodes": [
1
],
"system": "BandGeneral",
"constraints": "Plain",
"algorithm": "Newton",
"test": "NormDispIncr",
"tolerance": 1e-06,
"max_iter": 25
},
{
"id": 2,
"name": "Modal-3",
"type": "Modal",
"n_modes": 3,
"solver": "genBandArpack"
}
]
}

131
examples/sdof_pushover.py Normal file
View file

@ -0,0 +1,131 @@
"""SDOF cantilever column with a plastic hinge — pushover demo.
A 3 m steel column fixed at the base. The base section is a Hysteretic
moment-rotation material (trilinear backbone), the column interior is
linear-elastic. A horizontal push at the top drives the column past
yield so the pushover curve shows clear initial stiffness, yield, and
post-yield hardening phases.
Run from the repository root:
python examples/sdof_pushover.py
Produces ``examples/sdof_pushover.osmodel``.
Open in the GUI, run the "Push-X" case, then:
Display → Show Pushover Curve
→ you should see:
- linear segment from origin (slope = elastic stiffness)
- knee around yield moment / H
- post-yield flat-ish segment to the target displacement
Tip: the model also has a matching gravity-only Static case and a
modal case so you can exercise every Display feature on one model.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
ElasticBeamColumn,
ElasticSection,
HystereticMaterial,
LinearTimeSeries,
ModalCase,
NodalLoad,
Node,
PlainLoadPattern,
Project,
ProjectMeta,
PushoverCase,
UnitSystem,
)
from otko.services import load_project, save_project
def build_sdof() -> Project:
return Project(
meta=ProjectMeta(name="SDOF Pushover", author="Ozan",
units=UnitSystem.SI_M_N),
ndm=3, ndf=6,
nodes=[
Node(id=1, name="Base", coords=(0.0, 0.0, 0.0),
restraint=(True,) * 6),
Node(id=2, name="Top", coords=(0.0, 0.0, 3.0),
mass=(5_000.0, 5_000.0, 5_000.0, 0.0, 0.0, 0.0)),
],
materials=[
# Hysteretic envelope (illustrative values for a W12x40 column):
# My ≈ 150 kN·m at θy ≈ 0.01 rad;
# M_ult ≈ 165 kN·m at θ_ult ≈ 0.05 rad.
HystereticMaterial(
id=1, name="HingeSteel",
s1p=50e3, e1p=0.002,
s2p=150e3, e2p=0.01,
s3p=165e3, e3p=0.05,
s1n=-50e3, e1n=-0.002,
s2n=-150e3, e2n=-0.01,
s3n=-165e3, e3n=-0.05,
),
],
sections=[
ElasticSection(
id=1, name="W12x40",
E=200e9, A=0.0076,
Iz=2.0e-4, Iy=4.5e-5,
G=80e9, J=8.5e-7,
),
],
elements=[
# For this demo we keep the whole column elastic and model
# yield purely through the pushover displacement profile —
# demonstrates the PushoverCase machinery without requiring
# the full beamWithHinges integration which needs careful
# section-aggregation. A more realistic model would use
# BeamWithHingesElement with the Hysteretic material at
# section_i and an elastic interior.
ElasticBeamColumn(id=1, name="Col", nodes=(1, 2), section_id=1),
],
time_series=[LinearTimeSeries(id=1, name="Ramp")],
load_patterns=[
# Unit reference load at the top — the DisplacementControl
# integrator doesn't need the magnitude to be correct, it
# just scales it. OpenSees still needs SOME pattern loaded.
PlainLoadPattern(
id=1, name="PushRef",
time_series_id=1,
nodal_loads=[NodalLoad(node_id=2,
forces=(1.0, 0, 0, 0, 0, 0))],
),
],
analyses=[
PushoverCase(
id=1, name="Push-X",
pattern_ids=[1],
control_node=2, control_dof=1,
target_disp=0.1, step_size=0.001,
base_nodes=[1],
),
ModalCase(id=2, name="Modal-3", n_modes=3),
],
)
def main() -> None:
project = build_sdof()
project.validate_references()
print(f"Built '{project.meta.name}' — {len(project.nodes)} nodes, "
f"{len(project.elements)} elements, {len(project.analyses)} cases.")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

196
examples/space_frame_3d.py Normal file
View file

@ -0,0 +1,196 @@
"""Two-story 3D space frame — exercises full 3D rendering and dynamics.
Floor plan (each story):
N3 ──── N4 z (up)
│ │ │
│ │ │
N1 ──── N2 └──── x y → into page
Two stories @ 3 m, two-bay @ 5 m. 12 nodes, 20 elements
(8 columns + 8 floor beams + 4 stiffening braces in the bottom story).
Run from the repository root:
python examples/space_frame_3d.py
Produces ``examples/space_frame_3d.osmodel``.
Open in the GUI, run the cases, then exercise:
Display → Show Force Diagram → N (axial) → braces in tension/compression
Display → Show Force Diagram → M3 → moment distribution at columns
Display → Animate Mode Shape → 1st = sway, 2nd = perpendicular sway
Display → Time-History Plot → roof-corner displacement vs time
"""
from __future__ import annotations
import math
from pathlib import Path
from otko.core import (
ElasticBeamColumn,
ElasticSection,
LinearTimeSeries,
ModalCase,
NodalLoad,
Node,
PathTimeSeries,
PlainLoadPattern,
Project,
ProjectMeta,
ResponseSpectrum,
ResponseSpectrumCase,
StaticCase,
TransientCase,
UnitSystem,
)
from otko.services import load_project, save_project
def _earthquake_pulse(n_steps: int, dt: float) -> list[float]:
"""4-cycle damped sinusoid (toy 'ground motion')."""
f0 = 2.0 # Hz — close to the building's first period
zeta = 0.05
out = []
for i in range(n_steps):
t = i * dt
amp = math.exp(-2.0 * math.pi * f0 * zeta * t)
out.append(amp * math.sin(2.0 * math.pi * f0 * t))
return out
def build_space_frame() -> Project:
# ── geometry ──
bay_x, bay_y, story_z = 5.0, 5.0, 3.0
nodes = []
nid = 1
# 4 base nodes (z=0) — fully fixed.
for x in (0.0, bay_x):
for y in (0.0, bay_y):
nodes.append(Node(id=nid, name=f"Base{nid}",
coords=(x, y, 0.0), restraint=(True,) * 6))
nid += 1
# 4 first-floor + 4 roof nodes — free, with mass.
for story in (1, 2):
for x in (0.0, bay_x):
for y in (0.0, bay_y):
nodes.append(Node(
id=nid, name=f"L{story}N{nid}",
coords=(x, y, story * story_z),
mass=(2_500.0, 2_500.0, 2_500.0, 0.0, 0.0, 0.0),
))
nid += 1
# ── elements ──
elements: list[ElasticBeamColumn] = []
eid = 1
def add_el(ni: int, nj: int, sec: int, name: str) -> None:
nonlocal eid
elements.append(ElasticBeamColumn(id=eid, name=name,
nodes=(ni, nj), section_id=sec))
eid += 1
# Columns: bases (1-4) → 1st floor (5-8); 1st floor → roof (9-12).
for i in range(4):
add_el(i + 1, i + 5, sec=1, name=f"Col-G{i+1}")
add_el(i + 5, i + 9, sec=1, name=f"Col-1{i+1}")
# Floor beams at each story (5-8 and 9-12). Connect 4 nodes around perimeter.
for story_base in (5, 9):
a, b, c, d = story_base, story_base + 1, story_base + 2, story_base + 3
add_el(a, b, sec=2, name=f"Beam-{story_base}-X1")
add_el(c, d, sec=2, name=f"Beam-{story_base}-X2")
add_el(a, c, sec=2, name=f"Beam-{story_base}-Y1")
add_el(b, d, sec=2, name=f"Beam-{story_base}-Y2")
return Project(
meta=ProjectMeta(name="Space Frame 3D", author="Ozan", units=UnitSystem.SI_M_N),
ndm=3, ndf=6,
nodes=nodes,
sections=[
ElasticSection(id=1, name="HSS-Column",
E=200e9, A=0.012, Iz=2.5e-4, Iy=2.5e-4,
G=80e9, J=4.0e-4),
ElasticSection(id=2, name="W-Beam",
E=200e9, A=0.009, Iz=3.0e-4, Iy=8.0e-5,
G=80e9, J=1.0e-6),
],
elements=elements,
time_series=[
LinearTimeSeries(id=1, name="Ramp"),
PathTimeSeries(id=2, name="EQGround",
values=_earthquake_pulse(400, 0.01),
dt=0.01),
],
load_patterns=[
# Lateral push at the 4 roof nodes (X direction) for Static.
PlainLoadPattern(
id=1, name="StaticPush",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=9, forces=(25_000.0, 0, 0, 0, 0, 0)),
NodalLoad(node_id=10, forces=(25_000.0, 0, 0, 0, 0, 0)),
NodalLoad(node_id=11, forces=(25_000.0, 0, 0, 0, 0, 0)),
NodalLoad(node_id=12, forces=(25_000.0, 0, 0, 0, 0, 0)),
],
),
# Earthquake-style horizontal load on roof corner for Transient.
PlainLoadPattern(
id=2, name="EQRoofLoad",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=12, forces=(50_000.0, 0, 0, 0, 0, 0)),
],
),
],
analyses=[
StaticCase(id=1, name="Lateral-Push", pattern_ids=[1]),
ModalCase(id=2, name="Modal-6", n_modes=6),
TransientCase(id=3, name="EQ-4s", pattern_ids=[2],
dt=0.01, n_steps=400,
# ~5% damping at the first two modes (assuming
# f1 ≈ 2.5 Hz, f2 ≈ 5.0 Hz from typical 2-story
# steel frames). Solve 2x2 Rayleigh:
# α = 4π · f1·f2 · ζ / (f1 + f2)
# β = ζ / (π · (f1 + f2))
rayleigh_alpha_m=0.524,
rayleigh_beta_k=0.00106),
ResponseSpectrumCase(
id=4, name="RS-X-SRSS",
modal_case_id=2, spectrum_id=1, direction=1,
combination="SRSS",
),
],
spectra=[
# Approximated EC8 Type-1 elastic spectrum, soil class B,
# ag = 0.30g, S = 1.20, TB = 0.15s, TC = 0.50s, TD = 2.0s.
# Sa(T) values precomputed at a sparse grid; in real use
# you'd load these from a CSV or compute on the fly.
ResponseSpectrum(
id=1, name="EC8 Type-1 / Soil B",
periods=[0.01, 0.15, 0.50, 1.0, 2.0, 4.0],
accelerations=[3.53, 8.83, 8.83, 4.42, 2.21, 1.10],
damping_ratio=0.05,
),
],
)
def main() -> None:
project = build_space_frame()
project.validate_references()
print(f"Built '{project.meta.name}' — {len(project.nodes)} nodes, "
f"{len(project.elements)} elements, {len(project.analyses)} cases.")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,400 @@
0.01 -4.55879 -0.522462 2.20432 4.20647 -0.939194 1.4187 4.55879 0.522462 -2.20432 -4.20647 -5.67376 -2.98609
0.02 -42.1154 0.281186 12.7594 33.0601 2.53446 20.4451 42.1154 -0.281186 -12.7594 -33.0601 -40.8126 -19.6016
0.03 -190.26 24.7737 22.6167 128.275 65.7253 128.662 190.26 -24.7737 -22.6167 -128.275 -133.575 -54.3411
0.04 -573.278 143.455 -35.0299 338.749 364.021 499.194 573.278 -143.455 35.0299 -338.749 -258.932 -68.8278
0.05 -1327.02 470.358 -294.568 696.843 1194.26 1385.56 1327.02 -470.358 294.568 -696.843 -310.561 25.5099
0.06 -2563.82 1100.09 -887.239 1200.56 2838.17 2997.42 2563.82 -1100.09 887.239 -1200.56 -176.453 302.841
0.07 -4345.72 2033.48 -1840.54 1818.49 5377.86 5357.47 4345.72 -2033.48 1840.54 -1818.49 143.766 742.97
0.08 -6650.56 3170.35 -3055.67 2511.1 8642.11 8272.96 6650.56 -3170.35 3055.67 -2511.1 524.912 1238.1
0.09 -9341.25 4380.25 -4395.03 3243.58 12340.1 11446.6 9341.25 -4380.25 4395.03 -3243.58 844.967 1694.13
0.1 -12173.5 5572.96 -5783.3 3982.64 16234.8 14606.6 12173.5 -5572.96 5783.3 -3982.64 1115.06 2112.3
0.11 -14862.7 6694.54 -7209.55 4684.42 20175.3 17532.9 14862.7 -6694.54 7209.55 -4684.42 1453.3 2550.68
0.12 -17179.8 7670.19 -8638.26 5290.48 23988 20000.4 17179.8 -7670.19 8638.26 -5290.48 1926.79 3010.19
0.13 -19005 8383.16 -9947.26 5740.23 27396.5 21762.5 19005 -8383.16 9947.26 -5740.23 2445.23 3386.98
0.14 -20302.7 8731.77 -10977.6 5988.56 30097.1 22638.1 20302.7 -8731.77 10977.6 -5988.56 2835.59 3557.19
0.15 -21050.3 8702.6 -11641.9 6014.76 31910.7 22608.9 21050.3 -8702.6 11641.9 -6014.76 3014.97 3498.93
0.16 -21191.3 8368.14 -11963.2 5817.62 32826.4 21797.3 21191.3 -8368.14 11963.2 -5817.62 3063.29 3307.14
0.17 -20647.8 7802.26 -11994.7 5404.97 32870.1 20325.3 20647.8 -7802.26 11994.7 -5404.97 3114.01 3081.47
0.18 -19364.9 7005.38 -11710.3 4790.16 31942.5 18199.6 19364.9 -7005.38 11710.3 -4790.16 3188.52 2816.56
0.19 -17341.1 5924.19 -10992.3 3997.55 29816.3 15352.3 17341.1 -5924.19 10992.3 -3997.55 3160.59 2420.31
0.2 -14626.3 4545.15 -9733.85 3069.11 26315 11799.3 14626.3 -4545.15 9733.85 -3069.11 2886.53 1836.14
0.21 -11314 2959.46 -7951.57 2062.7 21501.4 7750.64 11314 -2959.46 7951.57 -2062.7 2353.31 1127.73
0.22 -7550.16 1331.61 -5794.15 1041.18 15694.7 3561.49 7550.16 -1331.61 5794.15 -1041.18 1687.7 433.331
0.23 -3545.78 -190.447 -3448.81 60.5342 9317.31 -418.876 3545.78 190.447 3448.81 -60.5342 1029.12 -152.464
0.24 439.505 -1530.3 -1046.72 -834.951 2728.06 -3954.22 -439.505 1530.3 1046.72 834.951 412.098 -636.666
0.25 4153.58 -2660.78 1346.04 -1610.38 -3815.51 -6910.9 -4153.58 2660.78 -1346.04 1610.38 -222.619 -1071.44
0.26 7414.89 -3551.57 3667.01 -2237.14 -10082.1 -9193.37 -7414.89 3551.57 -3667.01 2237.14 -918.952 -1461.35
0.27 10136.6 -4157.92 5809.53 -2694.97 -15801.4 -10729.5 -10136.6 4157.92 -5809.53 2694.97 -1627.23 -1744.23
0.28 12298.9 -4461.26 7642.54 -2977.94 -20691.7 -11522.9 -12298.9 4461.26 -7642.54 2977.94 -2235.94 -1860.94
0.29 13900.2 -4506.21 9069.34 -3098.44 -24541.4 -11692.1 -13900.2 4506.21 -9069.34 3098.44 -2666.6 -1826.55
0.3 14928.4 -4383.37 10058 -3084.54 -27248.6 -11428.9 -14928.4 4383.37 -10058 3084.54 -2925.49 -1721.24
0.31 15368 -4172.37 10615.8 -2972.52 -28781.2 -10901.6 -15368 4172.37 -10615.8 2972.52 -3066.18 -1615.52
0.32 15227.3 -3905.03 10743.7 -2799.83 -29116 -10198.8 -15227.3 3905.03 -10743.7 2799.83 -3115.12 -1516.32
0.33 14557 -3584.3 10425.4 -2602.54 -28233.7 -9364.27 -14557 3584.3 -10425.4 2602.54 -3042.54 -1388.64
0.34 13452.2 -3232.81 9661.5 -2415.59 -26182.2 -8478.26 -13452.2 3232.81 -9661.5 2415.59 -2802.32 -1220.18
0.35 12044.3 -2916.1 8512.48 -2271.83 -23145.8 -7694.77 -12044.3 2916.1 -8512.48 2271.83 -2391.61 -1053.52
0.36 10490.5 -2717.35 7106.15 -2197.68 -19449 -7197.43 -10490.5 2717.35 -7106.15 2197.68 -1869.49 -954.632
0.37 8959.01 -2691.77 5602.59 -2207.51 -15485.5 -7117.42 -8959.01 2691.77 -5602.59 2207.51 -1322.33 -957.881
0.38 7605.53 -2843.76 4146.49 -2300.85 -11624.4 -7484.79 -7605.53 2843.76 -4146.49 2300.85 -815.04 -1046.5
0.39 6543.37 -3139.72 2840.76 -2463.98 -8150.68 -8238.04 -6543.37 3139.72 -2840.76 2463.98 -371.612 -1181.12
0.4 5822.78 -3532.7 1750.5 -2674.19 -5258.54 -9260.75 -5822.78 3532.7 -1750.5 2674.19 7.04466 -1337.34
0.41 5431.14 -3972.19 920.892 -2903.17 -3076.36 -10404.9 -5431.14 3972.19 -920.892 2903.17 313.688 -1511.65
0.42 5313.35 -4398.04 388.051 -3118.44 -1687.03 -11496.6 -5313.35 4398.04 -388.051 3118.44 522.877 -1697.5
0.43 5398.77 -4737.92 174.806 -3284.27 -1128.23 -12348.5 -5398.77 4737.92 -174.806 3284.27 603.81 -1865.3
0.44 5619.5 -4921.21 278.816 -3364.48 -1379.13 -12792.9 -5619.5 4921.21 -278.816 3364.48 542.685 -1970.74
0.45 5913.18 -4900.02 665.023 -3327.33 -2349.48 -12719.2 -5913.18 4900.02 -665.023 3327.33 354.407 -1980.89
0.46 6214.71 -4658.38 1268.58 -3150.6 -3881.08 -12085.5 -6214.71 4658.38 -1268.58 3150.6 75.3346 -1889.67
0.47 6445.96 -4202.42 2004.98 -2824.21 -5760.14 -10897.9 -6445.96 4202.42 -2004.98 2824.21 -254.788 -1709.4
0.48 6512.04 -3543.52 2779.14 -2350.18 -7731.85 -9179.33 -6512.04 3543.52 -2779.14 2350.18 -605.57 -1451.23
0.49 6308.2 -2690.95 3488.58 -1741.23 -9512.51 -6956.76 -6308.2 2690.95 -3488.58 1741.23 -953.247 -1116.1
0.5 5736.42 -1659.18 4023.89 -1019.91 -10804.3 -4274.8 -5736.42 1659.18 -4023.89 1019.91 -1267.35 -702.736
0.51 4726.39 -480.664 4275.28 -218.076 -11323.2 -1220.43 -4726.39 480.664 -4275.28 218.076 -1502.62 -221.568
0.52 3253.39 788.091 4150.26 624.292 -10842.7 2064.03 -3253.39 -788.091 -4150.26 -624.292 -1608.11 300.242
0.53 1346.48 2071.72 3596.07 1462.28 -9239.41 5389.59 -1346.48 -2071.72 -3596.07 -1462.28 -1548.79 825.575
0.54 -915.287 3287.93 2612.98 2250.66 -6517.54 8545.64 915.287 -3287.93 -2612.98 -2250.66 -1321.4 1318.16
0.55 -3416.1 4359.65 1248.81 2948.35 -2796.22 11330 3416.1 -4359.65 -1248.81 -2948.35 -950.222 1748.93
0.56 -6021.14 5224.01 -420.406 3521.43 1728.49 13575.7 6021.14 -5224.01 420.406 -3521.43 -467.274 2096.3
0.57 -8593.63 5838.18 -2308.08 3944.75 6820.41 15169.9 8593.63 -5838.18 2308.08 -3944.75 103.832 2344.59
0.58 -11007.3 6182.55 -4320.93 4202.99 12217.4 16063.6 11007.3 -6182.55 4320.93 -4202.99 745.427 2484.03
0.59 -13151.6 6260.85 -6351.85 4291.77 17625.8 16269.8 13151.6 -6260.85 6351.85 -4291.77 1429.74 2512.71
0.6 -14931.3 6096.81 -8275.57 4218.48 22718.4 15852.3 14931.3 -6096.81 8275.57 -4218.48 2108.33 2438.08
0.61 -16261 5728.16 -9957.18 4001.84 27151.6 14908.3 16261 -5728.16 9957.18 -4001.84 2719.91 2276.18
0.62 -17064.9 5199.98 -11270.8 3669.7 30602.3 13551.6 17064.9 -5199.98 11270.8 -3669.7 3210.04 2048.37
0.63 -17281.6 4559.44 -12116 3255.7 32803.6 11901.4 17281.6 -4559.44 12116 -3255.7 3544.58 1776.97
0.64 -16877.1 3853.49 -12424.8 2795.88 33567.8 10078.1 16877.1 -3853.49 12424.8 -2795.88 3706.7 1482.38
0.65 -15858.8 3129.19 -12161.3 2325.75 32798.7 8204.53 15858.8 -3129.19 12161.3 -2325.75 3685.22 1183.05
0.66 -14281.5 2434.69 -11323.9 1877.97 30501.6 6406.54 14281.5 -2434.69 11323.9 -1877.97 3470.14 897.528
0.67 -12244.2 1817.54 -9951.93 1479.88 26794.1 4807 12244.2 -1817.54 9951.93 -1479.88 3061.64 645.617
0.68 -9876.44 1318.83 -8129.78 1151.14 21906.9 3510.61 9876.44 -1318.83 8129.78 -1151.14 2482.47 445.886
0.69 -7321.24 964.658 -5980.29 901.905 16161.4 2583.73 7321.24 -964.658 5980.29 -901.905 1779.45 310.245
0.7 -4719.29 759.295 -3646.77 732.165 9929.63 2038.4 4719.29 -759.295 3646.77 -732.165 1010.69 239.49
0.71 -2197.32 684.186 -1273.19 632.581 3590 1829.1 2197.32 -684.186 1273.19 -632.581 229.579 223.455
0.72 138.771 703.076 1007.3 586.21 -2500.44 1863.77 -138.771 -703.076 -1007.3 -586.21 -521.447 245.459
0.73 2206.19 770.183 3073.99 570.629 -8016.69 2023.3 -2206.19 -770.183 -3073.99 -570.629 -1205.28 287.251
0.74 3943.88 837.626 4817.83 560.143 -12670.8 2181.82 -3943.88 -837.626 -4817.83 -560.143 -1782.71 331.057
0.75 5310.01 860.648 6146.16 528.206 -16224.8 2222.79 -5310.01 -860.648 -6146.16 -528.206 -2213.64 359.155
0.76 6281.14 801.41 6993.81 450.173 -18514.2 2050.17 -6281.14 -801.41 -6993.81 -450.173 -2467.17 354.061
0.77 6855.09 632.231 7334.03 306.126 -19470.4 1595.51 -6855.09 -632.231 -7334.03 -306.126 -2531.67 301.186
0.78 7056.06 337.668 7182.84 83.2521 -19132.1 820.712 -7056.06 -337.668 -7182.84 -83.2521 -2416.4 192.291
0.79 6938.29 -85.6035 6595.45 -222.716 -17641.1 -283.417 -6938.29 85.6035 -6595.45 222.716 -2145.21 26.6066
0.8 6583.99 -630.673 5658.91 -607.091 -15227.3 -1700.78 -6583.99 630.673 -5658.91 607.091 -1749.44 -191.241
0.81 6094.14 -1281.88 4484.55 -1056.63 -12187.9 -3391.68 -6094.14 1281.88 -4484.55 1056.63 -1265.8 -453.946
0.82 5574.51 -2014.14 3200.2 -1550.47 -8862.53 -5290.55 -5574.51 2014.14 -3200.2 1550.47 -738.053 -751.869
0.83 5121.02 -2791.02 1939.84 -2061.64 -5602.77 -7302.15 -5121.02 2791.02 -1939.84 2061.64 -216.756 -1070.92
0.84 4808.64 -3564.46 830.155 -2559.07 -2737.03 -9302.45 -4808.64 3564.46 -830.155 2559.07 246.571 -1390.92
0.85 4685.51 -4278.58 -23.1668 -3010.3 -538.726 -11148.4 -4685.51 4278.58 23.1668 3010.3 608.226 -1687.31
0.86 4772.31 -4877.2 -544.715 -3384.37 795.568 -12695.9 -4772.31 4877.2 544.715 3384.37 838.576 -1935.66
0.87 5065.23 -5311.97 -692.376 -3654.56 1154.57 -13819.7 -5065.23 5311.97 692.376 3654.56 922.559 -2116.22
0.88 5540.38 -5548.26 -456.005 -3800.6 511.589 -14429 -5540.38 5548.26 456.005 3800.6 856.425 -2215.82
0.89 6157.9 -5567.76 144.501 -3810.22 -1078.73 -14475.8 -6157.9 5567.76 -144.501 3810.22 645.224 -2227.49
0.9 6864.51 -5368.66 1059.69 -3680.09 -3482.62 -13956.3 -6864.51 5368.66 -1059.69 3680.09 303.562 -2149.68
0.91 7594.67 -4964.57 2210.86 -3416.15 -6490.62 -12907.2 -7594.67 4964.57 -2210.86 3416.15 -141.953 -1986.55
0.92 8271.7 -4382.32 3493.21 -3033.23 -9827.38 -11398.5 -8271.7 4382.32 -3493.21 3033.23 -652.259 -1748.43
0.93 8810.92 -3658.75 4783.68 -2553.9 -13171.6 -9525.08 -8810.92 3658.75 -4783.68 2553.9 -1179.45 -1451.18
0.94 9126.1 -2836.93 5952.63 -2006.51 -16184.7 -7396.9 -9126.1 2836.93 -5952.63 2006.51 -1673.2 -1113.88
0.95 9138.79 -1962.79 6876.76 -1422.78 -18543.4 -5131.95 -9138.79 1962.79 -6876.76 1422.78 -2086.85 -756.422
0.96 8788.91 -1083.05 7450.57 -835.221 -19970.6 -2851.02 -8788.91 1083.05 -7450.57 835.221 -2381.11 -398.139
0.97 8043.46 -243.668 7595.28 -274.644 -20260.2 -673.312 -8043.46 243.668 -7595.28 274.644 -2525.62 -57.6927
0.98 6901.24 512.362 7265.3 232.101 -19295.5 1289.9 -6901.24 -512.362 -7265.3 -232.101 -2500.43 247.18
0.99 5392.45 1148.63 6452.18 664.046 -17058.4 2944.78 -5392.45 -1148.63 -6452.18 -664.046 -2298.16 501.11
1 3574.11 1639.78 5185.64 1007.56 -13630.9 4225.89 -3574.11 -1639.78 -5185.64 -1007.56 -1926.03 693.435
1.01 1523.05 1975.07 3530.73 1257.14 -9186.16 5105.13 -1523.05 -1975.07 -3530.73 -1257.14 -1406.04 820.08
1.02 -671.165 2159.63 1581.68 1415.42 -3972.84 5594.99 671.165 -2159.63 -1581.68 -1415.42 -772.206 883.902
1.03 -2912.08 2212.9 -546.357 1492.4 1705.38 5745.17 2912.08 -2212.9 546.357 -1492.4 -66.3136 893.525
1.04 -5100.65 2165.06 -2725.64 1504.09 7510.94 5633.61 5100.65 -2165.06 2725.64 -1504.09 665.991 861.558
1.05 -7138.77 2052.49 -4823.62 1470.61 13093.3 5354.53 7138.77 -2052.49 4823.62 -1470.61 1377.61 802.939
1.06 -8933.15 1913.18 -6710.6 1414.17 18110.1 5006.05 8933.15 -1913.18 6710.6 -1414.17 2021.74 733.504
1.07 -10400.3 1782.65 -8268.41 1356.81 22250.8 4679.41 10400.3 -1782.65 8268.41 -1356.81 2554.43 668.546
1.08 -11472.9 1690.76 -9399.98 1318.24 25261.2 4450.98 11472.9 -1690.76 9399.98 -1318.24 2938.73 621.292
1.09 -12105.7 1659.69 -10038.5 1313.87 26966.3 4377.44 12105.7 -1659.69 10038.5 -1313.87 3149.17 601.636
1.1 -12281.3 1702.99 -10153.7 1353.33 27286.5 4493.52 12281.3 -1702.99 10153.7 -1353.33 3174.67 615.46
1.11 -12012 1825.21 -9754.38 1439.57 26244.2 4811.09 12012 -1825.21 9754.38 -1439.57 3018.92 664.549
1.12 -11338.7 2021.78 -8885.99 1568.67 23959.2 5318.6 11338.7 -2021.78 8885.99 -1568.67 2698.77 746.75
1.13 -10326.1 2278.86 -7626.02 1730.3 20636.1 5980.51 10326.1 -2278.86 7626.02 -1730.3 2241.99 856.079
1.14 -9055 2573.55 -6076.64 1908.65 16544.7 6737.86 9055 -2573.55 6076.64 -1908.65 1685.23 982.798
1.15 -7614.13 2875.01 -4356.34 2084 11997.3 7511.21 7614.13 -2875.01 4356.34 -2084 1071.69 1113.82
1.16 -6092.97 3147 -2590.37 2234.55 7323.2 8207.23 6092.97 -3147 2590.37 -2234.55 447.923 1233.76
1.17 -4576.25 3351.74 -901.055 2338.53 2843.52 8728.61 4576.25 -3351.74 901.055 -2338.53 -140.357 1326.6
1.18 -3140.17 3454.49 601.271 2376.3 -1151.47 8985.78 3140.17 -3454.49 -601.271 -2376.3 -652.345 1377.68
1.19 -1849.49 3427.82 1825.59 2332.3 -4421.63 8907.98 1849.49 -3427.82 -1825.59 -2332.3 -1055.14 1375.47
1.2 -754.844 3254.82 2705.74 2196.57 -6791.83 8451.67 754.844 -3254.82 -2705.74 -2196.57 -1325.38 1312.78
1.21 110.228 2930.71 3204.37 1965.77 -8162.97 7604.76 -110.228 -2930.71 -3204.37 -1965.77 -1450.15 1187.38
1.22 731.946 2462.88 3315.59 1643.67 -8518.94 6386.66 -731.946 -2462.88 -3315.59 -1643.67 -1427.83 1001.98
1.23 1117.99 1869.64 3065.69 1241.02 -7928.28 4845.07 -1117.99 -1869.64 -3065.69 -1241.02 -1268.78 763.861
1.24 1297.27 1178.29 2511.33 774.837 -6538.88 3050.62 -1297.27 -1178.29 -2511.33 -774.837 -995.108 484.25
1.25 1317.17 422.739 1734.44 267.128 -4564.59 1090.86 -1317.17 -422.739 -1734.44 -267.128 -638.725 177.358
1.26 1238.36 -358.848 834.28 -256.818 -2264.83 -935.862 -1238.36 358.848 -834.28 256.818 -238.014 -140.682
1.27 1127.83 -1126.46 -82.1279 -770.402 80.2399 -2926.41 -1127.83 1126.46 82.1279 770.402 166.144 -452.977
1.28 1051.35 -1840.65 -909.792 -1247.78 2195.36 -4778.96 -1051.35 1840.65 909.792 1247.78 534.02 -742.996
1.29 1066.73 -2465.1 -1554.64 -1665.8 3833.91 -6399.66 -1066.73 2465.1 1554.64 1665.8 830.025 -995.649
1.3 1218.91 -2969.35 -1940.97 -2005.78 4797.7 -7709.69 -1218.91 2969.35 1940.97 2005.78 1025.21 -1198.35
1.31 1537.02 -3331.44 -2017.06 -2254.75 4951.74 -8652.25 -1537.02 3331.44 2017.06 2254.75 1099.43 -1342.07
1.32 2033.27 -3540.18 -1758.86 -2406.34 4233.53 -9198.27 -2033.27 3540.18 1758.86 2406.34 1043.04 -1422.27
1.33 2702.94 -3596.25 -1171.4 -2461.06 2656.55 -9349.4 -2702.94 3596.25 1171.4 2461.06 857.647 -1439.36
1.34 3524.83 -3511.98 -287.998 -2425.95 308.226 -9137.29 -3524.83 3511.98 287.998 2425.95 555.768 -1398.65
1.35 4462.11 -3309.57 832.55 -2313.73 -2657.13 -8619.18 -4462.11 3309.57 -832.55 2313.73 159.475 -1309.53
1.36 5463.76 -3018.29 2110.4 -2141.49 -6029.74 -7870.59 -5463.76 3018.29 -2110.4 2141.49 -301.475 -1184.29
1.37 6467.12 -2671.11 3450.54 -1929.05 -9559.79 -6976.59 -6467.12 2671.11 -3450.54 1929.05 -791.826 -1036.75
1.38 7401.89 -2301.39 4749.87 -1697.15 -12976.3 -6023.24 -7401.89 2301.39 -4749.87 1697.15 -1273.32 -880.934
1.39 8195.53 -1940.03 5905.4 -1465.61 -16008.6 -5090.13 -8195.53 1940.03 -5905.4 1465.61 -1707.59 -729.947
1.4 8779.59 -1613.2 6822.78 -1251.64 -18408.9 -4244.65 -8779.59 1613.2 -6822.78 1251.64 -2059.46 -594.943
1.41 9096.03 -1340.71 7424.37 -1068.44 -19973.1 -3537.79 -9096.03 1340.71 -7424.37 1068.44 -2300.04 -484.347
1.42 9102.68 -1134.89 7655.7 -924.199 -20557.9 -3001.36 -9102.68 1134.89 -7655.7 924.199 -2409.22 -403.302
1.43 8776.78 -999.94 7489.72 -821.574 -20092 -2646.42 -8776.78 999.94 -7489.72 821.574 -2377.17 -353.402
1.44 8116.56 -931.796 6928.3 -757.742 -18580.2 -2462.69 -8116.56 931.796 -6928.3 757.742 -2204.74 -332.693
1.45 7140.7 -918.457 6001.46 -724.914 -16101.4 -2419.48 -7140.7 918.457 -6001.46 724.914 -1902.98 -335.89
1.46 5886.39 -941.024 4764.41 -711.316 -12801.1 -2468.22 -5886.39 941.024 -4764.41 711.316 -1492.14 -354.847
1.47 4406.42 -975.507 3292.99 -702.524 -8878.99 -2547.22 -4406.42 975.507 -3292.99 702.524 -999.974 -379.299
1.48 2765.91 -995.397 1677.9 -683.027 -4574.03 -2588.26 -2765.91 995.397 -1677.9 683.027 -459.677 -397.932
1.49 1038.71 -974.735 18.0381 -637.878 -146.844 -2524.53 -1038.71 974.735 -18.0381 637.878 92.7293 -399.67
1.5 -696.502 -891.212 -1586.61 -554.285 4138.69 -2298.65 696.502 891.212 1586.61 554.285 621.13 -374.982
1.51 -2360.6 -728.806 -3041.99 -422.978 8033.98 -1869.44 2360.6 728.806 3041.99 422.978 1092 -316.977
1.52 -3878.94 -479.558 -4266.64 -239.25 11323.2 -1216.55 3878.94 479.558 4266.64 239.25 1476.77 -222.127
1.53 -5186.4 -144.331 -5197.39 -3.56374 13838.3 -342.415 5186.4 144.331 5197.39 3.56374 1753.85 -90.5777
1.54 -6232.14 267.375 -5793.77 278.309 15471.2 728.225 6232.14 -267.375 5793.77 -278.309 1910.06 73.8991
1.55 -6983.23 738.382 -6040.69 595.634 16180.4 1950.89 6983.23 -738.382 6040.69 -595.634 1941.63 264.252
1.56 -7426.62 1245.33 -5948.94 933.51 15992.5 3265.24 7426.62 -1245.33 5948.94 -933.51 1854.29 470.739
1.57 -7569.2 1760.49 -5553.39 1274.04 14997.6 4599.75 7569.2 -1760.49 5553.39 -1274.04 1662.56 681.703
1.58 -7435.99 2253.82 -4909.27 1597.8 13339.5 5877 7435.99 -2253.82 4909.27 -1597.8 1388.26 884.473
1.59 -7067 2695.16 -4086.69 1885.39 11201.8 7019.15 7067 -2695.16 4086.69 -1885.39 1058.31 1066.33
1.6 -6513.16 3056.4 -3164.47 2119.07 8791.05 7953.75 6513.16 -3056.4 3164.47 -2119.07 702.371 1215.46
1.61 -5832 3313.82 -2223.61 2284.19 6320.38 8619.55 5832 -3313.82 2223.61 -2284.19 350.464 1321.93
1.62 -5083.52 3450.12 -1341.04 2370.39 3992.47 8971.87 5083.52 -3450.12 1341.04 -2370.39 30.6642 1378.49
1.63 -4326.11 3456.08 -584.038 2372.39 1985.03 8986.94 4326.11 -3456.08 584.038 -2372.39 -232.915 1381.3
1.64 -3612.95 3331.59 -5.5004 2290.45 438.347 8664.44 3612.95 -3331.59 5.5004 -2290.45 -421.846 1330.32
1.65 -2988.46 3085.69 359.585 2130.26 -554.088 8027.73 2988.46 -3085.69 -359.585 -2130.26 -524.666 1229.32
1.66 -2485.34 2735.69 496.336 1902.44 -951.422 7121.55 2485.34 -2735.69 -496.336 -1902.44 -537.587 1085.53
1.67 -2122.06 2305.5 410.614 1621.62 -767.268 6007.6 2122.06 -2305.5 -410.614 -1621.62 -464.575 908.912
1.68 -1901.52 1823.28 128.05 1305.26 -67.323 4758.62 1901.52 -1823.28 -128.05 -1305.26 -316.826 711.226
1.69 -1811.06 1318.96 -308.517 972.123 1037.24 3451.9 1811.06 -1318.96 308.517 -972.123 -111.69 504.984
1.7 -1823.97 821.749 -843.008 640.87 2400.16 2162.82 1823.97 -821.749 843.008 -640.87 128.864 302.422
1.71 -1902.45 357.975 -1411.2 328.546 3853.41 959.351 1902.45 -357.975 1411.2 -328.546 380.176 114.575
1.72 -2001.6 -50.7154 -1946.42 49.3232 5222.24 -102.643 2001.6 50.7154 1946.42 -49.3232 617.032 -49.5031
1.73 -2073.9 -388.691 -2385.4 -186.493 6340.36 -982.749 2073.9 388.691 2385.4 186.493 815.834 -183.324
1.74 -2073.61 -647.208 -2673.48 -373.09 7063.89 -1658.28 2073.61 647.208 2673.48 373.09 956.543 -283.349
1.75 -1960.73 -824.792 -2768.97 -509.369 7282.7 -2125.24 1960.73 824.792 2768.97 509.369 1024.22 -349.135
1.76 -1704.2 -927.088 -2646.2 -598.731 6928.56 -2398.01 1704.2 927.088 2646.2 598.731 1010.03 -383.251
1.77 -1284.33 -966.131 -2297.15 -648.485 5979.68 -2507.44 1284.33 966.131 2297.15 648.485 911.767 -390.949
1.78 -694.433 -959.024 -1731.84 -668.922 4461.65 -2497.48 694.433 959.024 1731.84 668.922 733.853 -379.589
1.79 58.2867 -926.084 -977.296 -672.144 2445.01 -2420.39 -58.2867 926.084 977.296 672.144 486.881 -357.856
1.8 952.677 -888.619 -75.4912 -670.769 39.7288 -2331.02 -952.677 888.619 75.4912 670.769 186.745 -334.835
1.81 1954.48 -866.606 919.892 -676.62 -2613.05 -2280.78 -1954.48 866.606 -919.892 676.62 -146.629 -319.036
1.82 3018.2 -876.544 1947.46 -699.522 -5351.55 -2312.13 -3018.2 876.544 -1947.46 699.522 -490.842 -317.502
1.83 4090.05 -929.764 2942.74 -746.317 -8005.47 -2454.19 -4090.05 929.764 -2942.74 746.317 -822.752 -335.099
1.84 5111.74 -1031.36 3842.97 -820.172 -10408.7 -2720.02 -5111.74 1031.36 -3842.97 820.172 -1120.24 -374.065
1.85 6024.81 -1179.82 4591.83 -920.258 -12411.6 -3105.63 -6024.81 1179.82 -4591.83 920.258 -1363.88 -433.842
1.86 6775.13 -1367.31 5143.52 -1041.8 -13892.2 -3590.75 -6775.13 1367.31 -5143.52 1041.8 -1538.31 -511.177
1.87 7316.91 -1580.46 5465.97 -1176.51 -14764.5 -4140.94 -7316.91 1580.46 -5465.97 1176.51 -1633.36 -600.456
1.88 7615.96 -1801.68 5542.81 -1313.33 -14983.8 -4710.8 -7615.96 1801.68 -5542.81 1313.33 -1644.62 -694.224
1.89 7651.93 -2010.6 5374.1 -1439.5 -14548.7 -5247.96 -7651.93 2010.6 -5374.1 1439.5 -1573.56 -783.849
1.9 7419.41 -2185.94 4975.65 -1541.66 -13499.7 -5697.55 -7419.41 2185.94 -4975.65 1541.66 -1427.23 -860.271
1.91 6928.09 -2307.26 4377.32 -1607.19 -11914.5 -6006.98 -6928.09 2307.26 -4377.32 1607.19 -1217.44 -914.802
1.92 6201.92 -2356.87 3620.39 -1625.33 -9901.43 -6130.69 -6201.92 2356.87 -3620.39 1625.33 -959.739 -939.916
1.93 5277.51 -2321.51 2754.25 -1588.22 -7590.63 -6034.57 -5277.51 2321.51 -2754.25 1588.22 -672.109 -929.956
1.94 4201.9 -2193.73 1832.75 -1491.7 -5124.6 -5699.48 -4201.9 2193.73 -1832.75 1491.7 -373.65 -881.716
1.95 3029.52 -1972.8 910.393 -1335.74 -2648 -5123.6 -3029.52 1972.8 -910.393 1335.74 -83.176 -794.8
1.96 1818.78 -1664.98 38.5794 -1124.58 -297.834 -4323.18 -1818.78 1664.98 -38.5794 1124.58 182.096 -671.746
1.97 628.203 -1283.11 -737.825 -866.443 1805.76 -3331.46 -628.203 1283.11 737.825 866.443 407.717 -517.854
1.98 -487.552 -845.604 -1383.35 -572.937 3567.13 -2196.05 487.552 845.604 1383.35 572.937 582.934 -340.757
1.99 -1481.32 -374.907 -1874.04 -258.194 4920.79 -974.934 1481.32 374.907 1874.04 258.194 701.311 -149.788
2 -2316.49 104.382 -2198.53 62.2265 5834.6 268.346 2316.49 -104.382 2198.53 -62.2265 760.982 44.8003
2.01 -2969.08 567.459 -2358.22 372.531 6310.12 1469.72 2969.08 -567.459 2358.22 -372.531 764.546 232.654
2.02 -3428.75 991.203 -2366.34 657.888 6380.43 2569.49 3428.75 -991.203 2366.34 -657.888 718.606 404.117
2.03 -3698.63 1355.84 -2246.16 905.534 6105.45 3516.59 3698.63 -1355.84 2246.16 -905.534 633.011 550.934
2.04 -3794.26 1646.3 -2028.48 1105.68 5565.55 4272.06 3794.26 -1646.3 2028.48 -1105.68 519.889 666.835
2.05 -3741.55 1853.15 -1748.83 1252.17 4853.93 4811.51 3741.55 -1853.15 1748.83 -1252.17 392.56 747.937
2.06 -3574.35 1973.14 -1444.37 1342.78 4068.68 5126.45 3574.35 -1973.14 1444.37 -1342.78 264.433 792.969
2.07 -3331.49 2009.24 -1150.97 1379.26 3304.94 5224.43 3331.49 -2009.24 1150.97 -1379.26 147.974 803.283
2.08 -3053.67 1970.18 -900.552 1366.96 2647.84 5127.9 3053.67 -1970.18 900.552 -1366.96 53.8168 782.649
2.09 -2780.27 1869.54 -718.832 1314.19 2166.44 4871.77 2780.27 -1869.54 718.832 -1314.19 -9.94082 736.862
2.1 -2546.28 1724.38 -623.64 1231.39 1909.15 4499.98 2546.28 -1724.38 623.64 -1231.39 -38.2314 673.16
2.11 -2379.54 1553.56 -623.827 1130.1 1900.76 4061.14 2379.54 -1553.56 623.827 -1130.1 -29.2754 599.521
2.12 -2298.47 1375.93 -718.874 1021.87 2141.22 3603.88 2298.47 -1375.93 718.874 -1021.87 15.4063 523.909
2.13 -2310.68 1208.6 -899.22 917.223 2606.43 3172.26 2310.68 -1208.6 899.22 -917.223 91.2278 453.529
2.14 -2412.41 1065.36 -1147.28 824.774 3250.83 2801.89 2412.41 -1065.36 1147.28 -824.774 191.005 394.194
2.15 -2589.07 955.608 -1439.08 750.493 4011.63 2516.98 2589.07 -955.608 1439.08 -750.493 305.611 349.842
2.16 -2816.65 883.634 -1746.36 697.28 4814.31 2328.64 2816.65 -883.634 1746.36 -697.28 424.76 322.261
2.17 -3063.98 848.526 -2038.91 664.821 5578.87 2234.56 3063.98 -848.526 2038.91 -664.821 537.85 311.02
2.18 -3295.53 844.488 -2287.02 649.754 6226.27 2219.85 3295.53 -844.488 2287.02 -649.754 634.782 313.613
2.19 -3474.4 861.599 -2463.73 646.116 6684.47 2259.02 3474.4 -861.599 2463.73 -646.116 706.704 325.771
2.2 -3565.44 886.901 -2546.78 646.032 6893.73 2318.78 3565.44 -886.901 2546.78 -646.032 746.614 341.929
2.21 -3538.06 905.733 -2520.16 640.579 6810.67 2361.41 3538.06 -905.733 2520.16 -640.579 749.791 355.791
2.22 -3368.76 903.198 -2375.09 620.734 6411.22 2348.63 3368.76 -903.198 2375.09 -620.734 714.051 360.962
2.23 -3043.17 865.672 -2110.59 578.332 5691.96 2245.43 3043.17 -865.672 2110.59 -578.332 639.809 351.586
2.24 -2557.44 782.221 -1733.44 506.945 4670.33 2023.71 2557.44 -782.221 1733.44 -506.945 529.98 322.948
2.25 -1918.96 645.794 -1257.68 402.588 3383.31 1665.41 1918.96 -645.794 1257.68 -402.588 389.723 271.977
2.26 -1146.26 454.084 -703.682 264.206 1884.99 1164.65 1146.26 -454.084 703.682 -264.206 226.058 197.602
2.27 -268.071 209.939 -96.7917 93.9004 243.006 528.89 268.071 -209.939 96.7917 -93.9004 47.3691 100.928
2.28 678.453 -78.716 534.336 -103.145 -1465.85 -221.346 -678.453 78.716 -534.336 103.145 -137.159 -14.8023
2.29 1650.15 -399.449 1159.59 -319.001 -3160.62 -1053.91 -1650.15 399.449 -1159.59 319.001 -318.146 -144.439
2.3 2600.82 -736.313 1749.31 -543.591 -4761.32 -1927.59 -2600.82 736.313 -1749.31 543.591 -486.616 -281.345
2.31 3484.34 -1071.16 2276.13 -765.464 -6193.87 -2795.54 -3484.34 1071.16 -2276.13 765.464 -634.532 -417.94
2.32 4257.85 -1385.12 2716.57 -972.711 -7394.46 -3609.03 -4257.85 1385.12 -2716.57 972.711 -755.249 -546.328
2.33 4884.51 -1660.14 3052.28 -1153.9 -8313.01 -4321.47 -4884.51 1660.14 -3052.28 1153.9 -843.842 -658.936
2.34 5335.85 -1880.37 3271 -1299.01 -8915.68 -4892 -5335.85 1880.37 -3271 1299.01 -897.307 -749.119
2.35 5593.49 -2033.45 3366.93 -1400.2 -9186.17 -5288.68 -5593.49 2033.45 -3366.93 1400.2 -914.627 -811.676
2.36 5650.11 -2111.38 3340.89 -1452.39 -9125.95 -5490.87 -5650.11 2111.38 -3340.89 1452.39 -896.727 -843.254
2.37 5509.73 -2111.12 3199.95 -1453.71 -8753.53 -5490.76 -5509.73 2111.12 -3199.95 1453.71 -846.321 -842.586
2.38 5187.28 -2034.79 2956.8 -1405.52 -8102.71 -5293.81 -5187.28 2034.79 -2956.8 1405.52 -767.672 -810.574
2.39 4707.39 -1889.46 2628.81 -1312.31 -7220.14 -4918.2 -4707.39 1889.46 -2628.81 1312.31 -666.286 -750.189
2.4 4102.61 -1686.48 2236.88 -1181.21 -6162.09 -4393.23 -4102.61 1686.48 -2236.88 1181.21 -548.553 -666.207
2.41 3411.07 -1440.51 1804.08 -1021.46 -4990.88 -3756.76 -3411.07 1440.51 -1804.08 1021.46 -421.354 -564.785
2.42 2673.72 -1168.3 1354.18 -843.547 -3770.89 -3051.96 -2673.72 1168.3 -1354.18 843.547 -291.649 -452.938
2.43 1931.46 -887.205 910.249 -658.361 -2564.67 -2323.68 -1931.46 887.205 -910.249 658.361 -166.078 -337.934
2.44 1222.31 -613.791 493.248 -476.337 -1429.16 -1614.68 -1222.31 613.791 -493.248 476.337 -50.5892 -226.697
2.45 578.851 -362.487 120.877 -306.628 -412.503 -962.216 -578.851 362.487 -120.877 306.628 49.8732 -125.245
2.46 26.2444 -144.505 -193.356 -156.445 448.459 -395.281 -26.2444 144.505 193.356 156.445 131.608 -38.234
2.47 -419.17 32.9105 -440.737 -30.5712 1129.9 67.3761 419.17 -32.9105 440.737 30.5712 192.311 31.3554
2.48 -750.52 166.851 -617.634 68.8879 1621.76 418.175 750.52 -166.851 617.634 -68.8879 231.138 82.3769
2.49 -970.323 258.709 -725.447 142.508 1927.65 660.622 970.323 -258.709 725.447 -142.508 248.69 115.505
2.5 -1089.74 313.782 -770.261 193.312 2063.87 808.278 1089.74 -313.782 770.261 -193.312 246.912 133.067
2.51 -1127.23 340.538 -762.229 226.325 2057.76 882.883 1127.23 -340.538 762.229 -226.325 228.931 138.731
2.52 -1106.71 349.649 -714.728 247.952 1945.35 911.85 1106.71 -349.649 714.728 -247.952 198.83 137.096
2.53 -1055.36 352.834 -643.317 265.257 1768.58 925.304 1055.36 -352.834 643.317 -265.257 161.371 133.198
2.54 -1001.26 361.649 -564.577 285.191 1572.06 952.949 1001.26 -361.649 564.577 -285.191 121.674 131.997
2.55 -970.913 386.291 -494.872 313.856 1399.75 1021 970.913 -386.291 494.872 -313.856 84.8703 137.87
2.56 -986.958 434.546 -449.119 355.859 1291.61 1149.47 986.958 -434.546 449.119 -355.859 55.7521 154.167
2.57 -1066.21 510.967 -439.651 413.821 1280.53 1350.04 1066.21 -510.967 439.651 -413.821 38.4276 182.864
2.58 -1218.18 616.369 -475.254 488.083 1389.74 1624.75 1218.18 -616.369 475.254 -488.083 36.0207 224.354
2.59 -1444.21 747.69 -560.455 576.626 1630.94 1965.69 1444.21 -747.69 560.455 -576.626 50.4298 277.383
2.6 -1737.29 898.23 -695.111 675.225 2003.17 2355.53 1737.29 -898.23 695.111 -675.225 82.1659 339.156
2.61 -2082.64 1058.24 -874.334 777.802 2492.72 2769.15 2082.64 -1058.24 874.334 -777.802 130.284 405.582
2.62 -2458.77 1215.82 -1088.75 876.961 3073.85 3175.82 2458.77 -1215.82 1088.75 -876.961 192.409 471.647
2.63 -2839.25 1357.97 -1325.09 964.644 3710.42 3542.04 2839.25 -1357.97 1325.09 -964.644 264.863 531.861
2.64 -3194.66 1471.77 -1567.02 1032.86 4358.2 3834.55 3194.66 -1471.77 1567.02 -1032.86 342.872 580.762
2.65 -3494.9 1545.56 -1796.25 1074.39 4967.88 4023.29 3494.9 -1545.56 1796.25 -1074.39 420.864 613.397
2.66 -3711.48 1569.96 -1993.74 1083.5 5488.4 4084.1 3711.48 -1569.96 1993.74 -1083.5 492.824 625.772
2.67 -3819.71 1538.72 -2141.1 1056.39 5870.61 4000.96 3819.71 -1538.72 2141.1 -1056.39 552.696 615.21
2.68 -3800.66 1449.34 -2221.88 991.662 6070.81 3767.43 3800.66 -1449.34 2221.88 -991.662 594.816 580.604
2.69 -3642.72 1303.32 -2222.85 890.425 6054.22 3387.43 3642.72 -1303.32 2222.85 -890.425 614.329 522.532
2.7 -3342.62 1106.12 -2135.13 756.275 5797.81 2875.11 3342.62 -1106.12 2135.13 -756.275 607.585 443.243
2.71 -2905.94 866.767 -1954.98 595.04 5292.5 2253.81 2905.94 -866.767 1954.98 -595.04 572.456 346.486
2.72 -2346.85 597.201 -1684.33 414.341 4544.43 1554.37 2346.85 -597.201 1684.33 -414.341 508.569 237.229
2.73 -1687.35 311.314 -1330.87 223.014 3575.19 812.681 1687.35 -311.314 1330.87 -223.014 417.411 121.26
2.74 -955.814 23.8683 -907.76 30.4288 2420.97 66.8706 955.814 -23.8683 907.76 -30.4288 302.313 4.73418
2.75 -185.175 -250.654 -432.97 -154.219 1130.61 -645.648 185.175 250.654 432.97 154.219 168.297 -106.313
2.76 589.272 -499.168 71.786 -322.592 -237.155 -1291.07 -589.272 499.168 -71.786 322.592 21.7975 -206.436
2.77 1332.16 -710.95 582.358 -467.811 -1617.35 -1841.67 -1332.16 710.95 -582.358 467.811 -129.726 -291.182
2.78 2010.33 -878.385 1073.67 -584.922 -2942.74 -2277.75 -2010.33 878.385 -1073.67 584.922 -278.257 -357.404
2.79 2594.9 -997.415 1521.31 -671.19 -4148.19 -2588.79 -2594.9 997.415 -1521.31 671.19 -415.744 -403.455
2.8 3063.03 -1067.69 1903.16 -726.196 -5174.84 -2773.82 -3063.03 1067.69 -1903.16 726.196 -534.642 -429.247
2.81 3399.21 -1092.38 2200.82 -751.745 -5974.02 -2840.97 -3399.21 1092.38 -2200.82 751.745 -628.437 -436.18
2.82 3596.04 -1077.76 2400.87 -751.581 -6510.5 -2806.33 -3596.04 1077.76 -2400.87 751.581 -692.106 -426.945
2.83 3654.53 -1032.43 2495.8 -730.948 -6764.91 -2692.06 -3654.53 1032.43 -2495.8 730.948 -722.495 -405.226
2.84 3583.64 -966.515 2484.54 -696.04 -6735.06 -2524.21 -3583.64 966.515 -2484.54 696.04 -718.566 -375.332
2.85 3399.48 -890.642 2372.53 -653.388 -6436.1 -2330.15 -3399.48 890.642 -2372.53 653.388 -681.496 -341.776
2.86 3123.86 -814.95 2171.35 -609.234 -5899.42 -2135.99 -3123.86 814.95 -2171.35 609.234 -614.619 -308.857
2.87 2782.53 -748.167 1897.86 -568.96 -5170.39 -1964.23 -2782.53 748.167 -1897.86 568.96 -523.201 -280.265
2.88 2403.2 -696.833 1573.05 -536.616 -4305.08 -1831.74 -2403.2 696.833 -1573.05 536.616 -414.073 -258.761
2.89 2013.41 -664.753 1220.43 -514.58 -3366.15 -1748.32 -2013.41 664.753 -1220.43 514.58 -295.141 -245.938
2.9 1638.6 -652.715 864.392 -503.394 -2418.37 -1716.03 -1638.6 652.715 -864.392 503.394 -174.809 -242.113
2.91 1300.24 -658.51 528.405 -501.769 -1523.85 -1729.21 -1300.24 658.51 -528.405 501.769 -61.3623 -246.325
2.92 1014.52 -677.234 233.327 -506.775 -737.625 -1775.23 -1014.52 677.234 -233.327 506.775 37.6452 -256.471
2.93 791.341 -701.844 -4.09949 -514.172 -103.657 -1835.99 -791.341 701.844 4.09949 514.172 115.955 -269.54
2.94 633.896 -723.921 -172.438 -518.879 348.239 -1889.83 -633.896 723.921 172.438 518.879 169.074 -281.929
2.95 538.74 -734.535 -266.354 -515.507 604.454 -1913.79 -538.74 734.535 266.354 515.507 194.609 -289.815
2.96 496.379 -725.169 -286.95 -498.926 668.398 -1885.97 -496.379 725.169 286.95 498.926 192.454 -289.541
2.97 492.299 -688.581 -241.602 -464.809 560.014 -1787.75 -492.299 688.581 241.602 464.809 164.792 -277.989
2.98 508.374 -619.563 -143.312 -410.101 314.009 -1605.79 -508.374 619.563 143.312 410.101 115.926 -252.895
2.99 524.539 -515.509 -9.61436 -333.376 -23.09 -1333.44 -524.539 515.509 9.61436 333.376 51.9331 -213.091
3 520.598 -376.755 138.882 -235.039 -396.829 -971.608 -520.598 376.755 -138.882 235.039 -19.8179 -158.656
3.01 478.035 -206.658 280.217 -117.38 -749.385 -529.034 -478.035 206.658 -280.217 117.38 -91.2653 -90.9414
3.02 381.672 -11.4157 392.912 15.5606 -1024.43 -21.7453 -381.672 11.4157 -392.912 -15.5606 -154.312 -12.5018
3.03 221.056 200.38 457.794 158.262 -1171.89 528.044 -221.056 -200.38 -457.794 -158.262 -201.494 73.0957
3.04 -8.54265 418.389 459.632 304.136 -1152.29 1093.62 8.54265 -418.389 -459.632 -304.136 -226.605 161.544
3.05 -305.595 631.334 388.493 446.035 -940.254 1645.87 305.595 -631.334 -388.493 -446.035 -225.226 248.136
3.06 -662.274 827.898 240.67 576.796 -526.891 2155.55 662.274 -827.898 -240.67 -576.796 -195.119 328.142
3.07 -1064.91 997.613 19.1333 689.804 79.0518 2595.65 1064.91 -997.613 -19.1333 -689.804 -136.452 397.188
3.08 -1494.88 1131.69 -266.542 779.495 851.459 2943.47 1494.88 -1131.69 266.542 -779.495 -51.834 451.598
3.09 -1929.9 1223.68 -600.773 841.775 1748.48 3182.35 1929.9 -1223.68 600.773 -841.775 53.8434 488.677
3.1 -2345.6 1269.94 -963.051 874.317 2715.41 3302.92 2345.6 -1269.94 963.051 -874.317 173.747 506.914
3.11 -2717.21 1269.89 -1329.42 876.699 3688.67 3303.61 2717.21 -1269.89 1329.42 -876.699 299.6 506.07
3.12 -3021.33 1225.94 -1674.27 850.398 4600.5 3190.66 3021.33 -1225.94 1674.27 -850.398 422.309 487.173
3.13 -3237.65 1143.25 -1972.25 798.621 5384.11 2977.35 3237.65 -1143.25 1972.25 -798.621 532.653 452.399
3.14 -3350.39 1029.22 -2200.27 725.997 5978.8 2682.8 3350.39 -1029.22 2200.27 -725.997 622.011 404.869
3.15 -3349.46 892.854 -2339.25 638.162 6334.73 2330.2 3349.46 -892.854 2339.25 -638.162 683.035 348.365
3.16 -3231.23 743.955 -2375.72 541.279 6416.92 1944.86 3231.23 -743.955 2375.72 -541.279 710.247 287.008
3.17 -2998.83 592.325 -2302.88 441.517 6208.14 1552.07 2998.83 -592.325 2302.88 -441.517 700.49 224.906
3.18 -2661.95 446.958 -2121.24 344.561 5710.51 1175.05 2661.95 -446.958 2121.24 -344.561 653.2 165.821
3.19 -2236.22 315.339 -1838.7 255.168 4945.62 833.147 2236.22 -315.339 1838.7 -255.168 570.488 112.872
3.2 -1742.13 202.894 -1470.05 176.836 3953.14 540.38 1742.13 -202.894 1470.05 -176.836 457.003 68.3026
3.21 -1203.68 112.64 -1035.93 111.579 2788.16 304.581 1203.68 -112.64 1035.93 -111.579 319.612 33.3386
3.22 -646.761 45.0651 -561.394 59.8566 1517.29 127.062 646.761 -45.0651 561.394 -59.8566 166.895 8.13375
3.23 -97.4834 -1.75621 -74.1197 20.637 213.846 2.91065 97.4834 1.75621 74.1197 -20.637 8.51373 -8.17929
3.24 419.527 -31.8341 397.612 -8.39928 -1047.33 -78.1534 -419.527 31.8341 -397.612 8.39928 -145.504 -17.3488
3.25 882.654 -50.7479 826.941 -30.5472 -2195.31 -130.457 -882.654 50.7479 -826.941 30.5472 -285.512 -21.7865
3.26 1274.59 -65.0024 1190.36 -49.6805 -3168.11 -170.713 -1274.59 65.0024 -1190.36 49.6805 -402.966 -24.2946
3.27 1583.31 -81.3135 1469.39 -69.8116 -3917.11 -216.177 -1583.31 81.3135 -1469.39 69.8116 -491.056 -27.7639
3.28 1802.64 -105.892 1651.86 -94.6473 -4410.38 -282.806 -1802.64 105.892 -1651.86 94.6473 -545.204 -34.8699
3.29 1932.41 -143.788 1732.72 -127.182 -4634.77 -383.57 -1932.41 143.788 -1732.72 127.182 -563.392 -47.7948
3.3 1978.17 -198.353 1714.28 -169.363 -4596.54 -527.06 -1978.17 198.353 -1714.28 169.363 -546.289 -67.9999
3.31 1950.49 -270.868 1605.89 -221.863 -4320.51 -716.536 -1950.49 270.868 -1605.89 221.863 -497.161 -96.067
3.32 1863.94 -360.363 1423.11 -283.969 -3847.73 -949.467 -1863.94 360.363 -1423.11 283.969 -421.59 -131.624
3.33 1735.78 -463.659 1186.35 -353.612 -3232.04 -1217.62 -1735.78 463.659 -1186.35 353.612 -327.003 -173.357
3.34 1584.47 -575.601 919.197 -427.515 -2535.53 -1507.69 -1584.47 575.601 -919.197 427.515 -222.066 -219.115
3.35 1428.24 -689.488 646.511 -501.463 -1823.55 -1802.37 -1428.24 689.488 -646.511 501.463 -115.984 -266.089
3.36 1283.6 -797.647 392.406 -570.658 -1159.46 -2081.89 -1283.6 797.647 -392.406 570.658 -17.761 -311.051
3.37 1164.13 -892.097 178.357 -630.132 -599.584 -2325.65 -1164.13 892.097 -178.357 630.132 64.5124 -350.639
3.38 1079.46 -965.25 21.5219 -675.179 -188.863 -2514.1 -1079.46 965.25 -21.5219 675.179 124.297 -381.647
3.39 1034.64 -1010.58 -66.5799 -701.767 42.6277 -2630.42 -1034.64 1010.58 66.5799 701.767 157.112 -401.312
3.4 1029.86 -1023.19 -80.9179 -706.902 81.8785 -2662.02 -1029.86 1023.19 80.9179 706.902 160.875 -407.558
3.41 1060.55 -1000.28 -23.2787 -688.891 -66.2129 -2601.67 -1060.55 1000.28 23.2787 688.891 136.049 -399.178
3.42 1117.89 -941.384 97.9581 -647.515 -379.454 -2448.2 -1117.89 941.384 -97.9581 647.515 85.5795 -375.95
3.43 1189.6 -848.424 268.574 -584.058 -820.36 -2206.61 -1189.6 848.424 -268.574 584.058 14.6388 -338.657
3.44 1261.06 -725.603 469.733 -501.23 -1339.39 -1887.77 -1261.06 725.603 -469.733 501.23 -69.8136 -289.04
3.45 1316.58 -579.064 679.575 -402.959 -1879.11 -1507.54 -1316.58 579.064 -679.575 402.959 -159.611 -229.651
3.46 1340.72 -416.407 875.046 -294.09 -2379.08 -1085.56 -1340.72 416.407 -875.046 294.09 -246.054 -163.66
3.47 1319.69 -246.103 1033.82 -180.018 -2780.83 -643.712 -1319.69 246.103 -1033.82 180.018 -320.63 -94.5982
3.48 1242.51 -76.8343 1136.17 -66.2675 -3032.79 -204.419 -1242.51 76.8343 -1136.17 66.2675 -375.707 -26.0842
3.49 1102.02 83.1723 1166.59 41.913 -3094.6 211.061 -1102.02 -83.1723 -1166.59 -41.913 -405.167 38.4555
3.5 895.592 226.752 1115.15 139.937 -2940.54 584.212 -895.592 -226.752 -1115.15 -139.937 -404.901 96.0448
3.51 625.449 348.316 978.309 224.197 -2561.77 900.577 -625.449 -348.316 -978.309 -224.197 -373.155 144.372
3.52 298.686 444.213 759.312 292.29 -1967.25 1150.7 -298.686 -444.213 -759.312 -292.29 -310.681 181.941
3.53 -73.1215 512.918 467.982 343.14 -1183.26 1330.6 73.1215 -512.918 -467.982 -343.14 -220.689 208.156
3.54 -474.625 555.041 120.029 376.998 -251.485 1441.81 474.625 -555.041 -120.029 -376.998 -108.602 223.313
3.55 -887.692 573.139 -264.11 395.335 773.976 1490.88 887.692 -573.139 264.11 -395.335 18.3554 228.534
3.56 -1292.6 571.38 -660.804 400.631 1830.69 1488.53 1292.6 -571.38 660.804 -400.631 151.719 225.611
3.57 -1669.37 555.064 -1045 396.087 2852.57 1448.38 1669.37 -555.064 1045 -396.087 282.436 216.815
3.58 -1999.08 530.077 -1392.12 385.278 3774.8 1385.57 1999.08 -530.077 1392.12 -385.278 401.561 204.656
3.59 -2265.14 502.299 -1679.88 371.795 4538.7 1315.26 2265.14 -502.299 1679.88 -371.795 500.945 191.641
3.6 -2454.43 477.044 -1889.99 358.89 5096.12 1251.1 2454.43 -477.044 1889.99 -358.89 573.852 180.028
3.61 -2558.1 458.56 -2009.46 349.171 5412.91 1204.06 2558.1 -458.56 2009.46 -349.171 615.464 171.622
3.62 -2572.19 449.651 -2031.54 344.365 5471.38 1181.34 2572.19 -449.651 2031.54 -344.365 623.238 167.612
3.63 -2497.77 451.44 -1956.2 345.167 5271.51 1185.85 2497.77 -451.44 1956.2 -345.167 597.075 168.471
3.64 -2340.86 463.293 -1790.01 351.201 4830.72 1215.95 2340.86 -463.293 1790.01 -351.201 539.301 173.927
3.65 -2111.91 482.917 -1545.6 361.075 4182.33 1265.75 2111.91 -482.917 1545.6 -361.075 454.465 182.999
3.66 -1825.02 506.614 -1240.64 372.541 3372.97 1325.73 1825.02 -506.614 1240.64 -372.541 348.959 194.107
3.67 -1496.97 529.659 -896.432 382.73 2458.79 1383.74 1496.97 -529.659 896.432 -382.73 230.51 205.239
3.68 -1146 546.79 -536.261 388.452 1501.22 1426.23 1146 -546.79 536.261 -388.452 107.566 214.144
3.69 -790.562 552.736 -183.645 386.522 562.287 1439.64 790.562 -552.736 183.645 -386.522 -11.3522 218.569
3.7 -448.136 542.75 139.434 374.093 -299.995 1411.77 448.136 -542.75 -139.434 -374.093 -118.308 216.476
3.71 -134.06 513.1 414.128 348.952 -1035.85 1333.05 134.06 -513.1 -414.128 -348.952 -206.538 206.253
3.72 139.395 461.463 626.052 309.768 -1607.21 1197.51 -139.395 -461.463 -626.052 -309.768 -270.944 186.882
3.73 363.718 387.196 766.244 256.257 -1990.29 1003.54 -363.718 -387.196 -766.244 -256.257 -308.447 158.051
3.74 534.562 291.459 831.682 189.259 -2176.87 754.169 -534.562 -291.459 -831.682 -189.259 -318.174 120.208
3.75 651.822 177.177 825.321 110.71 -2174.5 456.984 -651.822 -177.177 -825.321 -110.71 -301.467 74.547
3.76 719.401 48.8507 755.674 23.5239 -2005.3 123.624 -719.401 -48.8507 -755.674 -23.5239 -261.722 22.9281
3.77 744.682 -87.7717 635.949 -68.6118 -1703.79 -231.053 -744.682 87.7717 -635.949 68.6118 -204.062 -32.2618
3.78 737.747 -226.129 482.848 -161.51 -1313.66 -590.097 -737.747 226.129 -482.848 161.51 -134.884 -88.2907
3.79 710.427 -359.351 315.108 -250.8 -884.01 -935.761 -710.427 359.351 -315.108 250.8 -61.3136 -142.294
3.8 675.232 -480.789 151.912 -332.254 -465.128 -1250.87 -675.232 480.789 -151.912 332.254 9.39131 -191.497
3.81 644.267 -584.519 11.3026 -402.109 -104.34 -1520.13 -644.267 584.519 -11.3026 402.109 70.432 -233.431
3.82 628.203 -665.797 -91.2875 -457.34 157.848 -1731.27 -628.203 665.797 91.2875 457.34 116.015 -266.118
3.83 635.391 -721.391 -144.262 -495.877 290.983 -1875.96 -635.391 721.391 144.262 495.877 141.804 -288.217
3.84 671.167 -749.803 -140.762 -516.735 277.031 -1950.3 -671.167 749.803 140.762 516.735 145.254 -299.113
3.85 737.42 -751.332 -79.1647 -520.059 111.705 -1955.05 -737.42 751.332 79.1647 520.059 125.789 -298.948
3.86 832.43 -727.991 36.8382 -507.072 -195.337 -1895.39 -832.43 727.991 -36.8382 507.072 84.8223 -288.579
3.87 951.005 -683.286 198.598 -479.936 -621.413 -1780.37 -951.005 683.286 -198.598 479.936 25.6176 -269.491
3.88 1084.89 -621.875 393.257 -441.544 -1132.77 -1621.98 -1084.89 621.875 -393.257 441.544 -46.9973 -243.651
3.89 1223.41 -549.144 604.822 -395.255 -1687.43 -1434.1 -1223.41 549.144 -604.822 395.255 -127.037 -213.327
3.9 1354.36 -470.73 815.481 -344.597 -2238.59 -1231.3 -1354.36 470.73 -815.481 344.597 -207.851 -180.89
3.91 1464.9 -392.047 1007.05 -292.976 -2738.47 -1027.53 -1464.9 392.047 -1007.05 292.976 -282.666 -148.612
3.92 1542.67 -317.845 1162.42 -243.392 -3142.12 -835.055 -1542.67 317.845 -1162.42 243.392 -345.124 -118.48
3.93 1576.71 -251.841 1266.93 -198.219 -3411 -663.481 -1576.71 251.841 -1266.93 198.219 -389.788 -92.041
3.94 1558.4 -196.457 1309.52 -159.035 -3515.99 -519.076 -1558.4 196.457 -1309.52 159.035 -412.559 -70.2931
3.95 1482.18 -152.684 1283.57 -126.537 -3439.72 -404.425 -1482.18 152.684 -1283.57 126.537 -410.993 -53.6262
3.96 1346.03 -120.078 1187.43 -100.539 -3177.82 -318.412 -1346.03 120.078 -1187.43 100.539 -384.472 -41.8218
3.97 1151.75 -96.891 1024.51 -80.0503 -2739.3 -256.564 -1151.75 96.891 -1024.51 80.0503 -334.238 -34.1091
3.98 904.875 -80.3156 803.026 -63.4291 -2145.8 -211.678 -904.875 80.3156 -803.026 63.4291 -263.281 -29.2687
3.99 614.436 -66.8278 535.34 -48.5903 -1429.93 -174.706 -614.436 66.8278 -535.34 48.5903 -176.092 -25.7778
4 292.362 -52.5864 237.028 -33.2531 -632.782 -135.78 -292.362 52.5864 -237.028 33.2531 -78.3008 -21.9797

Some files were not shown because too many files have changed in this diff Show more