Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
fail-fast: False
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.13", "3.14"]

steps:
- name: checkout repository
Expand All @@ -36,10 +36,10 @@ jobs:
activate-environment: test
auto-activate-base: false

- name: Download and install anba
- name: Download and install b3_secfem
run: |
git clone https://github.com/ANBA4/anba4.git
cd anba4
git clone git@github.com:gbarter/b3_secfem.git
cd b3_secfem
pip install -e .

- name: Install SONATA
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ conda activate sonata-env
cd ..
```

Next, in the same conda environment compile ANBA4 (open-source)
Next, in the same conda environment compile b3_secfem4 (open-source)

```
git clone git@github.com:ANBA4/anba4.git # (or git clone https://github.com/ANBA4/anba4.git)
cd anba4
git clone git@github.com:gbarter/b3_secfem4.git # (or git clone https://github.com/gbarter/b3_secfem4.git)
cd b3_secfem4
pip install -e .
cd ..
```
Expand Down Expand Up @@ -100,4 +100,4 @@ Detailed instructions can be found [here](examples/2_IEA22MW/README.md). Further

**Pflumm, T., Rex, W., Hajek, M.:** Propagation of Material and Manufacturing Uncertainties in Composite Helicopter Rotor Blades, 45th European Rotorcraft Forum, Warsaw, Poland, 2019 [[more…\]](https://mediatum.ub.tum.de/1520025) [BibTeX\]](https://mediatum.ub.tum.de/export/1520025/bibtex)

**Porter, J. H., Mace, T., Bortolotti, P., et al.:** Prediction of structural damping in a composite structure from coupon tests, 2025, Preprint. [[link]](https://doi.org/10.2139/ssrn.5408061)
**Porter, J. H., Mace, T., Bortolotti, P., et al.:** Novel application of the modal strain energy technique for state-of-the-art damping predictions, 2026, Composite Structures. [[link]](https://doi.org/10.1016/j.compstruct.2026.120394)
58 changes: 29 additions & 29 deletions SONATA/anbax/anbax_utl.py → SONATA/b3_secfem/b3_secfem_utl.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def dolfin_solve(cbm_mesh, cbm_nodes, cbm_materials):
"""function to generate the dolfin.Mesh from a SONATA-CBM definition to run
with anbax
with b3_secfem

Parameters
----------
Expand All @@ -24,7 +24,7 @@ def dolfin_solve(cbm_mesh, cbm_nodes, cbm_materials):
Returns
----------
mesh : dolfin.Mesh
matLibrary : vector of anbax materials
matLibrary : vector of b3_secfem materials
materials : dolfin.MeshFunction definign cell materials
plane_orientations : dolfin.MeshFunction defining cell plane orientations
fiber_orientations : dolfin.MeshFunction defining cell material fiber orientation
Expand All @@ -33,7 +33,7 @@ def dolfin_solve(cbm_mesh, cbm_nodes, cbm_materials):
Notes
----------
the cells of cbm_mesh already contain the nodes. So the information is
currently passed twice. But consistent with the export_cells_for_vabs.
currently passed twice.

"""
# Would like to avoid writing mesh to a file in the future, but for now just give dummy name
Expand All @@ -47,7 +47,7 @@ def dolfin_solve(cbm_mesh, cbm_nodes, cbm_materials):
coords = np.zeros((len(cbm_nodes), 2))
for n in cbm_nodes:
coords[n.id-1,:] = n.coordinates

n_cells = len(cbm_mesh)
tris = np.zeros((n_cells, 3), dtype=np.int32)
fiber_orientations = np.zeros(n_cells)
Expand All @@ -69,7 +69,7 @@ def dolfin_solve(cbm_mesh, cbm_nodes, cbm_materials):
materials_vec = [materials_vec[m] for m in oci]
fiber_orientations = fiber_orientations[oci]
plane_orientations = plane_orientations[oci]

# This meshtag doesn't do anything if not using the "region_mat" approach, but code complains if no tags are given
indices = np.arange(n_cells, dtype=np.int32)
values = np.array([c.id-1 for c in cbm_mesh], dtype=np.int32)
Expand All @@ -94,32 +94,32 @@ def dolfin_solve(cbm_mesh, cbm_nodes, cbm_materials):

# Keep oci tracker consistent
result.oci = oci[result.oci]

return result


def anbax_unit_recovery(anba, T=None):
def b3_secfem_unit_recovery(b3_secfem_in, T=None):
"""
Function to recover unit stresses and strains from an applied loading
Results are generated in global coordinates with dimensions: ([3 unit F + 3 unit M, num elements, 6 voigt])

INPUTS:
anba - dolfin construct from anbax
T - Transformation matrix to convert results from ANBA to SONATA/VABS coordinates
b3_secfem - dolfin construct from b3_secfem
T - Transformation matrix to convert results from b3_secfem to SONATA coordinates

OUTPUTS:
elem_stress_tran - global stress field in SONATA/VABS coordinates
elem_strain_tran - global strain field in SONATA/VABS coordinates
elem_stress_tran - global stress field in SONATA coordinates
elem_strain_tran - global strain field in SONATA coordinates
"""

fields = b3_secfem.recover_unit_load_strains(anba) #unit_load_
fields = b3_secfem.recover_unit_load_strains(b3_secfem_in) #unit_load_
stress = fields.sigma.copy() # dim: ([3 unit F + 3 unitM, num elements, 6 voigt])
stressM = fields.sigma_mat.copy() # dim: ([3 unit F + 3 unitM, num elements, 6 voigt])
strain = fields.epsilon.copy() # dim: ([3 unit F + 3 unitM, num elements, 6 voigt])

if T is None:
T = np.eye(3)

# Initialize the outputs:
elem_stress_tran = np.zeros(stress.shape)
elem_stressM_tran = np.zeros(stress.shape)
Expand Down Expand Up @@ -149,7 +149,7 @@ def anbax_unit_recovery(anba, T=None):
[strain[ii,k,4], strain[ii,k,3], strain[ii,k,2]],
])

# Rotate the matrix to SONATA/VABS coordinates
# Rotate the matrix to SONATA coordinates
istress = T.T @ elem_stress_mat[ ii,k,:,:] @ T
istressM = T.T @ elem_stressM_mat[ii,k,:,:] @ T
istrain = T.T @ elem_strain_mat[ ii,k,:,:] @ T
Expand All @@ -158,32 +158,32 @@ def anbax_unit_recovery(anba, T=None):
elem_stress_tran[ ii,k,:] = np.r_[np.diag(istress ), istress[ 1,2], istress[ 0,2], istress[ 0,1]]
elem_stressM_tran[ii,k,:] = np.r_[np.diag(istressM), istressM[1,2], istressM[0,2], istressM[0,1]]
elem_strain_tran[ ii,k,:] = np.r_[np.diag(istrain ), istrain[ 1,2], istrain[ 0,2], istrain[ 0,1]]
oci2orig = np.argsort(anba.oci)

oci2orig = np.argsort(b3_secfem_in.oci)
elem_stress_tran = elem_stress_tran[ :,oci2orig,:]
elem_stressM_tran = elem_stressM_tran[:,oci2orig,:]
elem_strain_tran = elem_strain_tran[ :,oci2orig,:]

return elem_stress_tran, elem_stressM_tran, elem_strain_tran


def anbax_recovery(anba, force, moment, T=None):
def b3_secfem_recovery(b3_secfem, force, moment, T=None):
"""
Function to recover total stresses and strains from an applied loading
Results are generated in global coordinates with dimensions: ([num elements, 6 voigt])

INPUTS:
anba - dolfin construct from anbax
force - Forces in anbax coordinates, [F1, F2, F3], e.g. force = [2.2, 3.4, 1.1]
moment - Moments in anbax coordinates, [M1, M2, M3], e.g. moment = [4.2, 5.7, 6.2]
T - Transformation matrix to convert results from ANBA to SONATA/VABS coordinates
b3_secfem - dolfin construct from b3_secfem
force - Forces in b3_secfem coordinates, [F1, F2, F3], e.g. force = [2.2, 3.4, 1.1]
moment - Moments in b3_secfem coordinates, [M1, M2, M3], e.g. moment = [4.2, 5.7, 6.2]
T - Transformation matrix to convert results from b3_secfem to SONATA coordinates

OUTPUTS:
stress_sum - global total stress field in SONATA/VABS coordinates
strain_sum - global total strain field in SONATA/VABS coordinates
stress_sum - global total stress field in SONATA coordinates
strain_sum - global total strain field in SONATA coordinates
"""
stress, stressM, strain = anbax_unit_recovery(anba, T=T)

stress, stressM, strain = b3_secfem_unit_recovery(b3_secfem, T=T)
n_el = stress.shape[1]

# Rotate forces and moments
Expand All @@ -192,18 +192,18 @@ def anbax_recovery(anba, force, moment, T=None):
else:
myforce = T.T @ force @ T
mymoment = T.T @ moment @ T

# Add up total stress and strain through super-position linear combo of unit forces and moments
stress_sum = np.zeros((n_el,6))
stressM_sum = np.zeros((n_el,6))
strain_sum = np.zeros((n_el,6))
for k in range(3):
stress_sum += myforce[ k] * stress[ k,:,:]
stress_sum += mymoment[k] * stress[3+k,:,:]

stressM_sum += myforce[ k] * stressM[ k,:,:]
stressM_sum += mymoment[k] * stressM[3+k,:,:]

strain_sum += myforce[ k] * strain[ k,:,:]
strain_sum += mymoment[k] * strain[3+k,:,:]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import numpy as np


class ANBAXConfig(object):
class b3_secfemConfig(object):

"""
this class contains the Configuration for a anbax
this class contains the Configuration for a b3_secfem

Attributes:
----------
Expand All @@ -29,7 +29,7 @@ class ANBAXConfig(object):
def __init__(self, **kw):
self.recover_flag = 0
self.ref_sys = "global"
self.voigt_convention = "anba"
self.voigt_convention = "b3_secfem"

if "recover_flag" in kw:
self.recover_flag = kw["recover_flag"]
Expand All @@ -40,4 +40,4 @@ def __init__(self, **kw):


if __name__ == "__main__":
test = ANBAXConfig(recover_flag=1)
test = b3_secfemConfig(recover_flag=1)
12 changes: 1 addition & 11 deletions SONATA/cbm/classBeamSectionalProps.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,7 @@

class BeamSectionalProps(object):
"""
this class stores the beam cross-sectional data and has methods to
read the result files from vabs

The coordinate system follows the definition of VABS and DYMORE.
For more information see the the vabs and dymore user manual.
the x/x1 axis is in the direction of the beam. the y/x2 axis points towards
the leading edge, and z/x3 points accordingly upwards.

this class stores the beam cross-sectional data
The 6x6 sectional stiffness matrix, TS (Timoshenko Stiffness Matrix)
(1-extension; 2,3-shear, 4-twist; 5,6-bending) relates the sectional axial
strain, epsilon1, transverse shearing strains, epsilon2 and epsilon3,
Expand Down Expand Up @@ -153,9 +146,6 @@ def __init__(self, fname=None):
self.ELE = None
self.U = None

if fname:
self.read_vabs_K(fname)

@property
def m00(self):
return self.MM[0, 0]
Expand Down
Loading
Loading