Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to get all mesh nodes for cut volume in Plaxis3DOutputController #26

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
125 changes: 125 additions & 0 deletions notebooks/Plaxis3D_output_controller.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Plaxis 3D Output Controller\n",
"\n",
"______________________________________________________________________\n",
"\n",
"**Authors: Pablo Vasconez & Daan Vink**"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1. Install additional requirements and import required modules"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!pip install -r requirements.txt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from plxscripting.easy import new_server\n",
"from plxcontroller.plaxis_3d_output_controller import Plaxis3DOutputController"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2. Activate scripting server in the PLAXIS 3D program (manually)\n",
"![image](image.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 3. Connect to the remote scripting server and create new Plaxis3DInputController instance"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Enter IP address of machine and port (integer) and password of the PLAXIS remote server\n",
"ip_address = \"localhost\" # can also be an IP address with format \"XXX.XXX.X.XX\"\n",
"port = 10000\n",
"password = \"<password in plaxis program>\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Connect to PLAXIS remote server\n",
"server, _ = new_server(ip_address, port, password=password)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create a new Plaxis controller instance\n",
"co = Plaxis3DOutputController(server)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 4. Start scripting"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a = co.get_nodes_per_cut_volume()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "env-notebook-local",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Empty file.
47 changes: 47 additions & 0 deletions src/plxcontroller/mesh_3d/node_3d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from __future__ import annotations

from plxcontroller.geometry_3d.point_3d import Point3D


class Node3D:
"""
A class with information about a node in a 3D mesh.
"""

def __init__(self, node_id: int, point: Point3D) -> None:
"""Initializes a Node3D instance.

Parameters
----------
node_id : int
the id of the node in the mesh.
point: Point3D
the point where the node is located in the 3D space.

Raises
------
TypeError
if any parameter is not of the expected type.
"""
# Validate types
if not isinstance(node_id, int):
raise TypeError(
f"Unexpected type found for node_id. Expected int, but got {type(node_id)}."
)
if not isinstance(point, Point3D):
raise TypeError(
f"Unexpected type found for point. Expected Point3D, but got {type(point)}."
)

self._node_id = node_id
self._point = point

@property
def node_id(self) -> int:
"""Returns the id of the node in the mesh."""
return self._node_id

@property
def point(self) -> Point3D:
"""Returns the point where the node is located in the 3D space."""
return self._point
70 changes: 70 additions & 0 deletions src/plxcontroller/plaxis_3d_output_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from __future__ import annotations

from typing import Dict, List

from plxscripting.plxproxy import PlxProxyGlobalObject
from plxscripting.server import Server

from plxcontroller.geometry_3d.point_3d import Point3D


class Plaxis3DOutputController:
def __init__(self, server: Server):
"""Creates a new Plaxis3DOutputController instance based on a server connection with the Plaxis program.

Args:
server (Server): the server connection with the Plaxis program.
"""
self.server = server

@property
def s_o(self) -> Server:
"""Returns the server object. This is a typical alias for the server object."""
return self.server

@property
def g_o(self) -> PlxProxyGlobalObject:
"""Returns the global project object. This is a typical alias for the global project object."""
return self.server.plx_global

def get_nodes_per_cut_volume(self) -> Dict[str, List[Point3D]]:
"""Get all the nodes per cut volume as points.

Note that cut volumes are the volumes produced by the intersection
of geometry in Plaxis (this takes place when the tab Mesh is clicked).

Returns
-------
Dict[str, List[Point3D]]
the dictionary with all the nodes per cut volume in the following
format {cut_volume_name: points}
"""
# Map all nodes per soil volume
nodes_per_cut_volume = {}
for volume in self.g_o.Volumes:
# Volumes deepest level (cutted in Mesh, present also in Stages)
for cut_volume in volume:
# Request x, y and z values
plaxis_xs = self.g_o.getresults(
cut_volume, self.g_o.ResultTypes.Soil.X, "node", False
)
plaxis_ys = self.g_o.getresults(
cut_volume, self.g_o.ResultTypes.Soil.Y, "node", False
)
plaxis_zs = self.g_o.getresults(
cut_volume, self.g_o.ResultTypes.Soil.Z, "node", False
)
# Map PlxValues to List[float] (this is time consuming)
xs = list(map(float, plaxis_xs))
ys = list(map(float, plaxis_ys))
zs = list(map(float, plaxis_zs))
# Make a set of the coordinates
coordinates_set = set()
for x, y, z in zip(xs, ys, zs):
coordinates_set.add((x, y, z))
# Store the coordinates as points
nodes_per_cut_volume[cut_volume.Name.value] = [
Point3D(x=c[0], y=c[1], z=c[2]) for c in list(coordinates_set)
]

return nodes_per_cut_volume
Empty file added tests/test_mesh_3d/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions tests/test_mesh_3d/test_node_3d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

from plxcontroller.geometry_3d.point_3d import Point3D
from plxcontroller.mesh_3d.node_3d import Node3D


def test_mesh_3d() -> None:
"""
Tests the methods of the class Mesh3D.
"""

# Assert invalid input
with pytest.raises(TypeError, match="Expected int"):
Node3D(node_id="invalid input", point=Point3D(x=1.0, y=2.0, z=3.0))

with pytest.raises(TypeError, match="Expected Point3D"):
Node3D(node_id=1, point="invalid input")

# Assert instance is correct with valid input
node = Node3D(node_id=1, point=Point3D(x=1.0, y=2.0, z=3.0))
assert node.node_id == 1
assert node.point.coordinates == (1.0, 2.0, 3.0)