Skip to content
Open
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
1 change: 1 addition & 0 deletions data/crossvault_form.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions data/extrados_mesh.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions data/intrados_mesh.json

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions scripts/TNO_integration/tno_arch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from compas_dem.templates import ArchTemplate
from compas_dem.models import SurfaceModel
from compas_dem.viewer import DEMViewer

# =============================================================================
# Template
# =============================================================================

template = ArchTemplate(rise=3, span=10, thickness=0.5, depth=2.5, n=50)

# =============================================================================
# Model
# =============================================================================

model = SurfaceModel.from_template(template)

# =============================================================================
# Viz
# =============================================================================

viewer = DEMViewer(model)

viewer.setup2()
viewer.show()
68 changes: 68 additions & 0 deletions scripts/TNO_integration/tno_cv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from compas_dem.templates.crossvault import CrossVaultTemplate
from compas_dem.models import SurfaceModel
from compas_dem.viewer import MasonryViewer
from compas_tno.analysis import Analysis
from compas_tno.diagrams import FormDiagram

# =============================================================================
# Template
# =============================================================================

xy_span = [[0.0, 10.0], [0.0, 10.0]]
thk = 0.50
min_lb = 0.0
n = 50

template = CrossVaultTemplate(
xy_span=xy_span,
thk=thk,
min_lb=min_lb,
n=n
)

# =============================================================================
# Model
# =============================================================================

model = SurfaceModel.from_template(template)

# Access as properties
print(f"Volume: {model.volume}")
print(f"Total Selfweight: {model.total_selfweight}")

# =============================================================================
# Diagram
# =============================================================================

form = FormDiagram.create_cross_form(
# xy_span=[[0.1, 9.9], [0.1, 9.9]],
xy_span=xy_span,
discretisation=10,
)
model.formdiagram = form

# model.apply_selfweight()

# for key in model.formdiagram.vertices():
# print(model.formdiagram.vertex_attribute(key, "pz"))

# User should set the formdiagram themselves

# =============================================================================
# Diagram
# =============================================================================

analysis = Analysis.create_minthrust_analysis(model, printout=True)
analysis.apply_selfweight()
analysis.apply_envelope()
analysis.set_up_optimiser()
analysis.run()

# =============================================================================
# Viz
# =============================================================================

viewer = MasonryViewer(model)

viewer.setup()
viewer.show()
70 changes: 70 additions & 0 deletions scripts/TNO_integration/tno_cv_to_dem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Interoperability between TNO and DEM

from compas_dem.templates.crossvault import CrossVaultTemplate
from compas_dem.models import SurfaceModel
from compas_dem.viewer import MasonryViewer
from compas_tno.analysis import Analysis
from compas_tno.diagrams import FormDiagram

# =============================================================================
# Template
# =============================================================================

xy_span = [[0.0, 10.0], [0.0, 10.0]]
thk = 0.50
min_lb = 0.0
n = 50

template = CrossVaultTemplate(
xy_span=xy_span,
thk=thk,
min_lb=min_lb,
n=n
)

# =============================================================================
# Model
# =============================================================================

model = SurfaceModel.from_template(template)

# Access as properties
print(f"Volume: {model.volume}")
print(f"Total Selfweight: {model.total_selfweight}")

# =============================================================================
# Diagram
# =============================================================================

form = FormDiagram.create_fan_form(
xy_span=xy_span,
discretisation=10,
)

model.formdiagram = form


# =============================================================================
# Diagram
# =============================================================================

analysis = Analysis.create_minthrust_analysis(model, printout=True)
analysis.apply_selfweight()
analysis.apply_envelope()
analysis.set_up_optimiser()
analysis.run()

# =============================================================================
# Make DEM blocks with the minimum thrust solution with variable thk
# =============================================================================

blockmodel = model.to_blocks(option="Dual")

# =============================================================================
# Viz
# =============================================================================

viewer = MasonryViewer(model)
viewer.add_model(blockmodel)
viewer.setup()
viewer.show()
45 changes: 45 additions & 0 deletions scripts/TNO_integration/tno_general-1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from compas.datastructures import Mesh
from compas_dem.models import SurfaceModel
from compas_dem.viewer import MasonryViewer
from compas_tno.analysis import Analysis
from compas_tno.diagrams import FormDiagram

intrados_json = "./data/intrados_mesh.json"
extrados_json = "./data/extrados_mesh.json"

intrados_mesh = Mesh.from_json(intrados_json)
extrados_mesh = Mesh.from_json(extrados_json)

model = SurfaceModel.from_meshes(intrados_mesh, extrados_mesh)

# =============================================================================
# Diagram
# =============================================================================

xy_span = [[0.0, 10.0], [0.0, 10.0]]
form = FormDiagram.create_cross_form(xy_span=xy_span, discretisation=10)
model.formdiagram = form

print(model.thickness)

model.apply_selfweight()

# Users should set the formdiagram themselves

# =============================================================================
# Analysis
# =============================================================================

analysis = Analysis.create_minthrust_analysis(model, printout=True)
analysis.apply_selfweight()
analysis.apply_envelope()
analysis.set_up_optimiser()
analysis.run()

# =============================================================================
# Viz
# =============================================================================

viewer = MasonryViewer(model)
viewer.setup()
viewer.show()
34 changes: 34 additions & 0 deletions scripts/TNO_integration/tno_general-2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This script creates a SurfaceModel from an existing 3D FormDiagram = ThrustNetwoek
# It then applies the thickness and visualizes it using the DEMViewer.
# It will be usefull if we are working from a TNA analysis and constraining it to further analysis.

import json
from compas.datastructures import Mesh
from compas_dem.models import SurfaceModel
from compas_dem.viewer import MasonryViewer
from compas_tno.analysis import Analysis
from compas_tno.diagrams import FormDiagram

# form_json = "./data/crossvault_form.json"
form_json = "./data/fan_form.json"

formdiagram = FormDiagram.from_json(form_json)
surfacemodel = SurfaceModel.from_formdiagram(formdiagram, thickness=0.75)

# =============================================================================
# Analysis
# =============================================================================

analysis = Analysis.create_minthrust_analysis(surfacemodel, printout=True)
analysis.apply_selfweight()
analysis.apply_envelope()
analysis.set_up_optimiser()
analysis.run()

# =============================================================================
# Viz
# =============================================================================

viewer = MasonryViewer(surfacemodel)
viewer.setup()
viewer.show()
4 changes: 2 additions & 2 deletions scripts/dem_arch.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from compas_dem.models import BlockModel
from compas_dem.templates import ArchTemplate
from compas_dem.viewer import DEMViewer
from compas_dem.viewer import MasonryViewer

# =============================================================================
# Template
Expand Down Expand Up @@ -31,7 +31,7 @@
# Viz
# =============================================================================

viewer = DEMViewer(model)
viewer = MasonryViewer(blockmodel=model)

viewer.setup()
viewer.show()
2 changes: 2 additions & 0 deletions src/compas_dem/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from .blockmodel import BlockModel
from .surfacemodel import SurfaceModel

__all__ = [
"BlockModel",
"SurfaceModel",
]
53 changes: 51 additions & 2 deletions src/compas_dem/models/blockmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def from_pavilionvault(cls) -> "BlockModel":
# =============================================================================

@classmethod
def from_triangulation_dual(cls, mesh: Mesh, lengthfactor: float = 1.0, tmin=None, tmax=None) -> "BlockModel":
def from_triangulation_dual(cls, mesh: Mesh, lengthfactor: float = 1.0, tmin=None, tmax=None, fixed_vertices=[]) -> "BlockModel":
"""Construct a Block Model from the dual of an isotropically remeshed triangulation of the input mesh.

Parameters
Expand All @@ -264,6 +264,8 @@ def from_triangulation_dual(cls, mesh: Mesh, lengthfactor: float = 1.0, tmin=Non
tmax : float, optional
Maximum thickness of the blocks.
If none is provided, the maximum thickness will be 50/1000 of the diagonal of the xy bounding box of the input mesh.
fixed_vertices : list[list[float]], optional
A list of fixed points on the target mesh.

Returns
-------
Expand All @@ -274,7 +276,7 @@ def from_triangulation_dual(cls, mesh: Mesh, lengthfactor: float = 1.0, tmin=Non
temp.quads_to_triangles()
M = temp.to_vertices_and_faces()

V1, F1, V2, F2 = trimesh_dual(M, length_factor=lengthfactor, number_of_iterations=100) # type: ignore
V1, F1, V2, F2 = trimesh_dual(M, length_factor=lengthfactor, number_of_iterations=100, fixed_vertices=fixed_vertices) # type: ignore
dual = Mesh.from_vertices_and_faces(V2, F2)
dual.unify_cycles()

Expand All @@ -299,6 +301,53 @@ def from_triangulation_dual(cls, mesh: Mesh, lengthfactor: float = 1.0, tmin=Non

return model


@classmethod
def from_dual(cls, mesh: Mesh, tmin=None, tmax=None, include_boundary=True) -> "BlockModel":
"""Construct a Block Model from the dual of an the input mesh. Note: It does not triangulate the mesh

Parameters
----------
mesh : :class:`Mesh`
The input mesh.
tmin : float, optional
Minimum thickness of the blocks.
If none is provided, the minimum thickness will be 3/1000 of the diagonal of the xy bounding box of the input mesh.
tmax : float, optional
Maximum thickness of the blocks.
If none is provided, the maximum thickness will be 50/1000 of the diagonal of the xy bounding box of the input mesh.
include_boundary : bool, optional
Whether to include boundary faces for the dual mesh.

Returns
-------
:class:`BlockModel`

"""
dual = mesh.dual(cls=Mesh, include_boundary=include_boundary)
dual.unify_cycles()

pattern_inverse_height_thickness(dual, tmin=tmin, tmax=tmax)
idos = pattern_idos(dual)
face_block: dict[int, Mesh] = pattern_blocks(dual, idos)

face: int
face_node: dict[int, int] = {}

model = cls()
for face, block in face_block.items():
node = model.add_block_from_mesh(block)
face_node[face] = node

for face in dual.faces(): # type: ignore
u = face_node[face]
nbrs = dual.face_neighbors(face)
for nbr in nbrs:
v = face_node[nbr]
model.graph.add_edge(u, v)

return model

@classmethod
def from_meshpattern(cls, mesh: Mesh, patternname: str, tmin=None, tmax=None, **kwargs) -> "BlockModel":
"""Construct a Block Model from the dual of an isotropically remeshed triangulation of the input mesh.
Expand Down
Loading