Skip to content

Utilize FourCIPP in MeshPy #354

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

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
2 changes: 1 addition & 1 deletion .github/actions/run_tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ inputs:
install-command:
description: Command to install MeshPy with pip
required: false
default: ".[dev]"
default: ".[dev,fourc]"
source-command:
description: Command to source the virtual environment
required: false
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
# non-editable installation and turn of coverage, because the coverage
# only works in editable mode.
install-command: >-
${{ matrix.python-version == '3.10' && '-e .[dev]' || '.[dev]'}}
${{ matrix.python-version == '3.10' && '-e .[dev,fourc]' || '.[dev,fourc]'}}
# The single space in the empty string is required, otherwise GitHub
# evaluates the if clause wrong.
additional-pytest-flags: >-
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
- name: Run the test suite
uses: ./.github/actions/run_tests
with:
install-command: ".[cubitpy,dev]"
install-command: ".[cubitpy,dev,fourc]"
source-command: "source python-workflow-venv/bin/activate"
additional-pytest-flags: "--CubitPy --no-cov"
- name: Upload test results on failure
Expand Down Expand Up @@ -96,7 +96,7 @@ jobs:
uses: ./.github/actions/run_tests
with:
source-command: "source python-workflow-venv/bin/activate"
install-command: "-e .[dev]"
install-command: "-e .[dev,fourc]"
additional-pytest-flags: "--4C --ArborX --cov-fail-under=92"
- name: Upload test results on failure
if: failure()
Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
- name: Run the test suite
uses: ./.github/actions/run_tests
with:
install-command: ".[cubitpy,dev]"
install-command: ".[cubitpy,dev,fourc]"
source-command: "source python-workflow-venv/bin/activate"
additional-pytest-flags: "--performance-tests --exclude-standard-tests -s --no-cov"
- name: Upload test results on failure
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dynamic = ["version"]

[project.optional-dependencies]
cubitpy = ["cubitpy@git+https://github.com/imcs-compsim/cubitpy.git@main"]
fourc = ["fourcipp@git+https://github.com/4c-multiphysics/fourcipp.git@main"]
fourc = ["fourcipp@git+https://github.com/4C-multiphysics/fourcipp.git@main"]
dev = [
"coverage-badge",
"coverage",
Expand Down
32 changes: 21 additions & 11 deletions src/meshpy/core/element_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@ class VolumeElement(_Element):
vtk_cell_type = None
vtk_topology: list = []

def __init__(self, nodes=None, string_pre_nodes="", string_post_nodes="", **kwargs):
def __init__(self, nodes=None, data={}, **kwargs):
super().__init__(nodes=nodes, material=None, **kwargs)
self.string_pre_nodes = string_pre_nodes
self.string_post_nodes = string_post_nodes
self.data = data

def dump_to_list(self):
"""Return a list with the items representing this object (usually a
single item)."""
"""Return a dict with the items representing this object."""

# String with the node ids.
nodes_string = " ".join(str(node.i_global) for node in self.nodes)

return [
f"{self.i_global} {self.string_pre_nodes} {nodes_string} {self.string_post_nodes}"
]
return {
"id": self.i_global,
"cell": {
"type": element_type_to_four_c_string[type(self)],
"connectivity": [node.i_global for node in self.nodes],
},
"data": self.data,
}

def get_vtk(self, vtk_writer_beam, vtk_writer_solid, **kwargs):
"""Add the representation of this element to the VTK writer as a
Expand Down Expand Up @@ -170,3 +170,13 @@ class VolumeHEX27(VolumeElement):
25,
26,
]


element_type_to_four_c_string = {
VolumeHEX8: "HEX8",
VolumeHEX20: "HEX20",
VolumeHEX27: "HEX27",
VolumeTET4: "TET4",
VolumeTET10: "TET10",
VolumeWEDGE6: "WEDGE6",
}
23 changes: 9 additions & 14 deletions src/meshpy/core/geometry_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@
"""This module implements a basic class to manage geometry in the input
file."""

import numpy as _np

from meshpy.core.base_mesh_item import BaseMeshItem as _BaseMeshItem
from meshpy.core.conf import mpy as _mpy
from meshpy.core.container import ContainerBase as _ContainerBase
from meshpy.core.element_beam import Beam as _Beam
from meshpy.core.node import Node as _Node
from meshpy.utils.environment import fourcipp_is_available as _fourcipp_is_available


class GeometrySetBase(_BaseMeshItem):
Expand Down Expand Up @@ -123,21 +120,19 @@ def get_all_nodes(self):
def dump_to_list(self):
"""Return a list with the legacy strings of this geometry set."""

if _fourcipp_is_available():
raise ValueError(
"Port this functionality to dump the geometry set to a suitable data format"
)
# Sort nodes based on their global index
nodes = sorted(self.get_all_nodes(), key=lambda n: n.i_global)

# Sort the nodes based on the node GID.
nodes = self.get_all_nodes()
if len(nodes) == 0:
if not nodes:
raise ValueError("Writing empty geometry sets is not supported")
nodes_id = [node.i_global for node in nodes]
sort_indices = _np.argsort(nodes_id)
nodes = [nodes[i] for i in sort_indices]

return [
f"NODE {node.i_global} {self.geometry_set_names[self.geometry_type]} {self.i_global}"
{
"type": "NODE",
"node_id": node.i_global,
"d_type": self.geometry_set_names[self.geometry_type],
"d_id": self.i_global,
}
for node in nodes
]

Expand Down
42 changes: 10 additions & 32 deletions src/meshpy/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import numpy as _np

from meshpy.core.base_mesh_item import BaseMeshItem as _BaseMeshItem
from meshpy.core.conf import mpy as _mpy
from meshpy.utils.environment import fourcipp_is_available as _fourcipp_is_available


class Node(_BaseMeshItem):
Expand Down Expand Up @@ -53,22 +51,6 @@ def __init__(self, coordinates, *, is_middle_node=False, **kwargs):
# If this node is replaced, store a link to the remaining node.
self.master_node = None

@classmethod
def from_legacy_string(cls, input_line):
"""Create the Node object from a legacy string."""

if _fourcipp_is_available():
raise ValueError(
"Port this functionality to create the node from the dict "
"representing the node, not the legacy string."
)

# Split up the input line.
line_split = input_line.split()

# Convert the node coordinates into a Node object.
return cls([float(line_split[i]) for i in range(3, 6)])

def get_master_node(self):
"""Return the master node of this node.

Expand Down Expand Up @@ -114,13 +96,11 @@ def rotate(self, *args, **kwargs):
def dump_to_list(self):
"""Return a list with the legacy string representing this node."""

if _fourcipp_is_available():
raise ValueError(
"Port this functionality to create a dict, not the legacy string."
)

coordinate_string = " ".join([str(item) for item in self.coordinates])
return [f"NODE {self.i_global} COORD {coordinate_string}"]
return {
"id": self.i_global,
"COORD": self.coordinates,
"data": {"type": "NODE"},
}


class NodeCosserat(Node):
Expand Down Expand Up @@ -169,10 +149,8 @@ def dump_to_list(self):
"""Return a list with the legacy string representing this control
point."""

if _fourcipp_is_available():
raise ValueError(
"Port this functionality to create a dict, not the legacy string."
)

coordinate_string = " ".join([str(item) for item in self.coordinates])
return [f"CP {self.i_global} COORD {coordinate_string} {self.weight}"]
return {
"id": self.i_global,
"COORD": self.coordinates,
"data": {"type": "CP", "weight": self.weight},
}
104 changes: 57 additions & 47 deletions src/meshpy/core/nurbs_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from meshpy.core.material import (
MaterialSolidBase as _MaterialSolidBase,
)
from meshpy.utils.environment import fourcipp_is_available as _fourcipp_is_available


class NURBSPatch(_Element):
Expand Down Expand Up @@ -72,26 +71,15 @@ def get_nurbs_dimension(self):
)
return n_knots

def dump_element_specific_section(self, yaml_dict):
def dump_element_specific_section(self, input_file):
"""Set the knot vectors of the NURBS patch in the input file."""

if _fourcipp_is_available():
raise ValueError("Port this functionality to not use the legacy format.")

knotvectors_section = "STRUCTURE KNOTVECTORS"

if knotvectors_section not in yaml_dict.keys():
yaml_dict[knotvectors_section] = []

section = yaml_dict[knotvectors_section]
section.append(f"NURBS_DIMENSION {self.get_nurbs_dimension()}")
section.append("BEGIN NURBSPATCH")
section.append(f"ID {self.n_nurbs_patch}")
patch_data = {
"knot_vectors": [],
}

for dir_manifold in range(len(self.knot_vectors)):
num_knotvectors = len(self.knot_vectors[dir_manifold])
section.append(f"NUMKNOTS {num_knotvectors}")
section.append(f"DEGREE {self.polynomial_orders[dir_manifold]}")

# Check the type of knot vector, in case that the multiplicity of the first and last
# knot vectors is not p + 1, then it is a closed (periodic) knot vector, otherwise it
Expand All @@ -115,12 +103,24 @@ def dump_element_specific_section(self, yaml_dict):
knotvector_type = "Periodic"
break

section.append(f"TYPE {knotvector_type}")
patch_data["knot_vectors"].append(
{
"DEGREE": self.polynomial_orders[dir_manifold],
"TYPE": knotvector_type,
"knots": [
knot_vector_val
for knot_vector_val in self.knot_vectors[dir_manifold]
],
}
)

for knot_vector_val in self.knot_vectors[dir_manifold]:
section.append(knot_vector_val)
if "STRUCTURE KNOTVECTORS" not in input_file:
input_file.add({"STRUCTURE KNOTVECTORS": []})
input_file["STRUCTURE KNOTVECTORS"] = []

section.append("END NURBSPATCH")
patches = input_file["STRUCTURE KNOTVECTORS"]
patch_data["ID"] = len(patches) + 1
patches.append(patch_data)

def get_number_elements(self):
"""Get the number of elements in this patch by checking the amount of
Expand Down Expand Up @@ -167,9 +167,6 @@ def dump_to_list(self):
"""Return a list with all the element definitions contained in this
patch."""

if _fourcipp_is_available():
raise ValueError("Port this functionality to not use the legacy format.")

# Check the material
self._check_material()

Expand Down Expand Up @@ -211,20 +208,29 @@ def get_ids_ctrlpts_surface(knot_span_u, knot_span_v):
):
element_cps_ids = get_ids_ctrlpts_surface(knot_span_u, knot_span_v)

string_cps = " ".join(
[str(self.nodes[i].i_global) for i in element_cps_ids]
connectivity = [self.nodes[i].i_global for i in element_cps_ids]

num_cp_in_element = (self.polynomial_orders[0] + 1) * (
self.polynomial_orders[1] + 1
)

patch_elements.append(
"{} {} NURBS{} {} MAT {} {}".format(
self.i_global + j,
self.element_string,
(self.polynomial_orders[0] + 1)
* (self.polynomial_orders[1] + 1),
string_cps,
self.material.i_global,
self.element_description,
)
{
"id": self.i_global + j,
"cell": {
"type": f"NURBS{num_cp_in_element}",
"connectivity": connectivity,
},
"data": {
"type": "WALLNURBS",
"MAT": self.material.i_global,
**(
self.element_description
if self.element_description
else {}
),
},
}
)
j += 1

Expand All @@ -243,9 +249,6 @@ def dump_to_list(self):
"""Return a list with all the element definitions contained in this
patch."""

if _fourcipp_is_available():
raise ValueError("Port this functionality to not use the legacy format.")

# Check the material
self._check_material()

Expand Down Expand Up @@ -302,9 +305,7 @@ def get_ids_ctrlpts_volume(knot_span_u, knot_span_v, knot_span_w):
knot_span_u, knot_span_v, knot_span_w
)

string_cps = " ".join(
[str(self.nodes[i].i_global) for i in element_cps_ids]
)
connectivity = [self.nodes[i].i_global for i in element_cps_ids]

num_cp_in_element = (
(self.polynomial_orders[0] + 1)
Expand All @@ -313,13 +314,22 @@ def get_ids_ctrlpts_volume(knot_span_u, knot_span_v, knot_span_w):
)

patch_elements.append(
"{} SOLID NURBS{} {} MAT {} {}".format(
self.i_global + increment_ele,
num_cp_in_element,
string_cps,
self.material.i_global,
self.element_description,
)
{
"id": self.i_global + increment_ele,
"cell": {
"type": f"NURBS{num_cp_in_element}",
"connectivity": connectivity,
},
"data": {
"type": "SOLID",
"MAT": self.material.i_global,
**(
self.element_description
if self.element_description
else {}
),
},
}
)
increment_ele += 1

Expand Down
Loading