Skip to content

Commit

Permalink
Merge branch 'claudioperez:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
claudioperez authored Aug 22, 2024
2 parents fcee3e9 + 8c05c45 commit ccb68b6
Show file tree
Hide file tree
Showing 13 changed files with 637 additions and 712 deletions.
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ Fast nonlinear finite element analysis.


`opensees` is a Python package that provides an intuitive API for nonlinear
finite element analysis, implemented in C++. The library features
state-of-the-art finite element formulations and solution algorithms, including
mixed formulations for beams and solids, over 200 material models, and an
finite element analysis, implemented in C++ through the OpenSees framework.
OpenSees features state-of-the-art finite element formulations and solution
algorithms, including mixed formulations for beams and solids, over 200 material models, and an
extensive collection of continuation algorithms to solve highly nonlinear
problems.

The package supports high quality interactive post processing via the
The `opensees` package supports high quality interactive post processing via the
[`sees`](https://pypi.org/project/sees) package.


Expand Down Expand Up @@ -87,18 +87,32 @@ Additional features include:
### Getting Started
The `opensees` package can be installed into a Python environment
in the standard manner. For example, using `pip`:
```shell
pip install opensees
```
There are several ways to use the `opensees` package:

- To execute Tcl procedures from a Python script, just create an instance
of the `opensees.tcl.Interpreter` class:
```
of the `opensees.tcl.Interpreter` class and call its `eval()` method:
```python
interp = opensees.tcl.Interpreter()
interp.eval("model Basic -ndm 2; print -json")
interp.eval("model Basic -ndm 2")
interp.eval("print -json")
```

- To start an interactive interpreter run the shell command:

```bash
python -m opensees
```
To quit the interpreter, just run `exit`:
```tcl
opensees > exit
```

- The `opensees` package exposes a compatibility layer that exactly reproduces
the *OpenSeesPy* functions, but does so without mandating a single
Expand All @@ -115,7 +129,7 @@ Additional features include:
`model` function; documentation is under development.


## Compiling
## Development

To compile the project see [help/compiling](https://github.com/claudioperez/opensees/blob/master/help/compiling.md)

Expand Down
2 changes: 1 addition & 1 deletion src/libg3
Submodule libg3 updated 1646 files
11 changes: 10 additions & 1 deletion src/opensees/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
#!/usr/bin/env python3
"""
This file implements the primary command line interface for
the package which is invoked by running:
python -m opensees
"""
import os
import sys

Expand Down Expand Up @@ -163,13 +170,15 @@ def parse_args(args):
for cmd in opts["commands"]:
tcl.eval(cmd)

# Store the exit code of the Tcl script
code = 0

#
if script is not None:
try:
code = tcl.eval(script)

except opensees.tcl.tkinter._tkinter.TclError as e:
except opensees.tcl.TclError as e:
print(e, file=sys.stderr)
if not opts["interact"]:
sys.exit(-1)
Expand Down
37 changes: 26 additions & 11 deletions src/opensees/emit/apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import textwrap
import importlib

from opensees.emit.writer import ModelWriter
from opensees.emit.emitter import Emitter, ScriptBuilder
from opensees.library import ast
from opensees.library.ast import Arg
Expand All @@ -13,19 +12,24 @@ def write_grp(a):
args = (write_grp(i) if isinstance(i, ast.Grp) else (i.name if i.name else "") for i in a.args)
return "["+",".join(args)+"]"

def write_obj(v, w, qual=None):
def write_obj(v, w, qual=None, ndm=None):

args = v._args if ndm is None else filter(lambda arg: arg.reqd or ndm in arg.ndm_reqd, v._args)

name = v.__name__
if qual is not None:
#qual = qual + "."
qual = qual + "(\""
else:
qual = ""
# s = "(" + ", ".join(arg.name for arg in v._args if arg.reqd) + ", **kwds)"
s = "\", " + ", ".join(arg.name for arg in v._args if arg.reqd) + ")" #", **kwds)"
s = "\", " + ", ".join(arg.name for arg in args) + ", **extra)"

#s = str(inspect.signature(v)).replace('=None','')

# if the signature is too long, add in line breaks
if len(s) > 45:
s = s.replace(", ", ",<br>&emsp;&emsp;&emsp;")
s = s.replace(", ", ",<br>&emsp;&emsp;&emsp;&emsp;")

w.write(textwrap.dedent(f"""
<span style="font-feature-settings: kern; color: var(--md-code-fg-color) !important; font-family: var(--md-code-font-family);">
Expand Down Expand Up @@ -150,11 +154,18 @@ class ApiDocWriter(ScriptBuilder):
def __init__(self):
ScriptBuilder.__init__(self, ApiEmitter)

def send(self, obj, idnt=None, qual=None):
def send(self, obj, idnt=None, qual=None, ndm=None):
w = self.streams[0]

write_obj(obj, w, qual=qual)
w.endln();
if ndm is not None:
for dm in ndm:
write_obj(obj, w, qual=qual, ndm=dm)
w.endln();

else:
write_obj(obj, w, qual=qual)
w.endln();


for arg in obj._args:
w.write("<tr>")
Expand All @@ -179,12 +190,16 @@ def send(self, obj, idnt=None, qual=None):
if __name__ == "__main__":
import opensees

_, *module, obj = sys.argv[1].split(".")
_, *module, name = sys.argv[1].split(".")
module = ".".join(module)

print(ApiDocWriter().send(
getattr(getattr(opensees, module), obj), qual="Model."+module
))
obj = getattr(getattr(opensees, module), name)

ndm = getattr(obj, "_ndms", None)

print(ApiDocWriter().send(obj, qual="Model."+module, ndm=ndm))





66 changes: 4 additions & 62 deletions src/opensees/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def Yld(nature="strength", about=None, **kwds):
def Area(**kwds):
return Num("A", field="area", about="cross-sectional area", **kwds)

def Izz(**kwds):
return Num("Iz", field="izz", about="area moment of inertia", **kwds)

Rho = lambda: None
class ConstitutiveData:
_args = [Yng(), Yld(), Rho()]
Expand Down Expand Up @@ -616,8 +619,6 @@ def create(cls, name, tag, *args):
# ])

class element:
Iyc = lambda: Num("iyc", field="iyc", about="Centroidal moment of inertia", alt="section")
Ixc = lambda: Num("ixc", field="ixc", about="", alt="section")

ZeroLength = ZeroLength3D = Ele("ZeroLength3D",
"zeroLength",
Expand Down Expand Up @@ -711,7 +712,7 @@ def section(self):
def init(self):
pass

DisplBeamColumn = dispBeamColumn = Ele("DispBeamColumn", "dispBeamColumn",
DisplBeamColumn = Ele("DispBeamColumn", "dispBeamColumn",
#, eleTag, *eleNodes, transfTag, integrationTag, '-cMass', '-mass', mass=0.0)
about="Create a dispBeamColumn element.",
args=[
Expand All @@ -730,65 +731,6 @@ def init(self):
refs=["transform"],
)

ElasticBeam2D = Ele("ElasticBeam2D",
"elasticBeamColumn",
args = [
Tag(),
Grp("nodes", args=[
Ref("iNode", type=Node, attr="name", about=""),
Ref("jNode", type=Node, attr="name", about=""),
]),
Area(alt="section"),
Yng( alt="material"),
Iyc(),
Ixc(),
Ref("geom", field="transform", type=Trf, attr="name", default=LinearTransform()),
Num("mass",field="mass_density", flag="-mass", default=0.0, reqd=False,
about="element mass per unit length"),
Flg("-cMass", field="consistent_mass",
about="Flag indicating whether to use consistent mass matrix.")
],
refs=["transform"],
alts=[
Ref("material", type="Material"),
Ref("section", type=Sec)
],
inherit=[_LineElement],
)

ElasticBeamColumn3D = Ele("ElasticBeamColumn3D",
"elasticBeamColumn",
args = [
Tag(),
Grp("nodes", args=[
Ref("iNode", type=Node, attr="name", about=""),
Ref("jNode", type=Node, attr="name", about=""),
]),
Area(alt="section"),
Yng( alt="material"),
Num("G", field="shear_modulus", about="", alt="material"),
Num("J", field="torsion_modulus", about="", alt="section"),
#Grp("moi", ctype="struct", args=[
#Num("iyc", field="iyc", about="Centroidal moment of inertia", alt="section"),
#Num("ixc", field="ixc", about="", alt="section"),
Iyc(),
Ixc(),
#]),
Ref("geom", field="transform", type=Trf, attr="name"),
Num("mass",field="mass_density", flag="-mass", default=0.0, reqd=False,
about="element mass per unit length"),
Flg("-cMass", field="consistent_mass",
about="Flag indicating whether to use consistent mass matrix.")
],
refs=["transform"],
alts=[
Ref("material", type="Material"),
Ref("section", type=Sec)
],
inherit=[_LineElement],
)



class constraint:
RigidBeamLink = Lnk("RigidBeamLink",
Expand Down
37 changes: 22 additions & 15 deletions src/opensees/library/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,34 @@ def __repr__(self):
return f"${self.name}"

class Arg:
__slots__ = ["name", "flag", "value", "field", "default", "type", "reqd", "namespace", "kwds", "about"]#, "group"]
__slots__ = ["name", "flag", "value", "field", "default", "type",
"reqd", "ndm_reqd", "ndm_only",
"namespace", "kwds", "about"]#, "group"]
def __init__(self,
name = None,
#help = None,
flag = None,
reqd = True,
type = None,
field= None,
about= "",
default = None,
# group = None,
flag: str = None,
reqd: bool = True,
ndm_reqd = (),
ndm_only = (),
type = None,
field: str = None,
about = "",
default = None,
# group = None,
**kwds
):
if name and name[0] == "-":
flag = name
name = name[1:]
self.name = name
self.flag = flag if flag is not None else ""
self.value = None
self.field = field if field is not None else name
self.type = type
self.reqd = reqd
self.name = name
self.flag = flag if flag is not None else ""
self.value = None
self.field = field if field is not None else name
self.type = type
self.reqd = reqd
self.ndm_reqd = ndm_reqd
self.ndm_only = ndm_only
self.default = default
self.kwds = kwds
self.about = about
Expand Down Expand Up @@ -65,7 +71,8 @@ def as_tcl_list(self, value=None)->list:
return []
else:
value = f"${value.name}"
return [self.flag] + [value]

return [self.flag] + [value]

def m_src(self, value=None):
value = self._get_value(None,value)
Expand Down
11 changes: 6 additions & 5 deletions src/opensees/library/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ def block(self, divs, type: str, args, points, **kwds):
Ref(f"Node{1+i}", type=Node, attr="name", about="")
for i in range(4)
])
arg_spec = [Tag(), nodes]+[Str(f"a{i+1}") for i in range(len(args))]
elem_class = Ele(type, type, args=arg_spec)

if len(self.m_nodes) > 0:
join = dict(nodes={int(v.name): v.crd for k,v in self.m_nodes.items()}, cells=self.m_elems.keys())
Expand All @@ -113,20 +111,23 @@ def block(self, divs, type: str, args, points, **kwds):
join = None


nodes, elems = shps.block.block(divs, block_type, points=points,
nodes, cells = shps.block.block(divs, block_type, points=points,
append=False, join=join, **kwds)


for tag, coord in nodes.items():
self.node(tag, *coord)

for tag, nodes in elems.items():
arg_spec = [Tag(), nodes]+[Str(f"a{i+1}") for i in range(len(args))]
elem_class = Ele(type, type, args=arg_spec)
for tag, nodes in cells.items():
elem = elem_class([], *args, name=tag)
# for node in nodes:
# print(node, self.get_node(node))
# print("")
self.elem(elem, list(map(int,nodes)), tag=tag)



def apply(self, prototypes=None, **kwds):
if prototypes is None:
prototypes = self.prototypes
Expand Down
Loading

0 comments on commit ccb68b6

Please sign in to comment.