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
56 changes: 30 additions & 26 deletions src/cubitpy/cubit_to_fourc_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,30 @@
from cubitpy.conf import cupy


def add_node_sets(cubit, exo, input_file, write_topology_information=True):
def add_node_sets(
cubit, exo, input_file, write_topology_information=True, use_exo_ids=False
):
"""Add the node sets contained in the cubit session/exo file to the yaml
file."""

# If there are no node sets we can return immediately
if len(cubit.node_sets) == 0:
return

# Get names of the node sets
names = []
for string_list in exo.variables["ns_names"]:
string = ""
for char in string_list:
# Get a mapping between the node set IDs and the node set names and keys in the exo file.
node_set_id_to_exo_name = {}
node_set_keys = [key for key in exo.variables.keys() if "node_ns" in key]
for i in range(len(exo.variables["ns_prop1"])):
node_set_id = int(exo.variables["ns_prop1"][i])
node_set_key = node_set_keys[i]
node_set_name = ""
for char in exo.variables["ns_names"][i]:
if isinstance(char, np.bytes_):
string += char.decode("UTF-8")
names.append(string)
node_set_name += char.decode("UTF-8")
node_set_id_to_exo_name[node_set_id] = {
"key": node_set_key,
"name": node_set_name,
}

# Sort the sets into their geometry type
node_sets = {
Expand All @@ -54,20 +62,18 @@ def add_node_sets(cubit, exo, input_file, write_topology_information=True):
cupy.geometry.surface: [],
cupy.geometry.volume: [],
}
boundary_condition_map = {}
node_set_keys = [key for key in exo.variables.keys() if "node_ns" in key]
for i_set, key in enumerate(node_set_keys):
bc_section, bc_description, geometry_type = cubit.node_sets[i_set]
node_sets[geometry_type].append(exo.variables[key][:])
bc_key = (bc_section, geometry_type)
if bc_key not in boundary_condition_map.keys():
boundary_condition_map[bc_key] = []
boundary_condition_map[bc_key].append(
[len(node_sets[geometry_type]), bc_description, names[i_set]]
)
for node_set_id, node_set_data in cubit.node_sets.items():
bc_section, bc_description, geometry_type = node_set_data
node_set_key = node_set_id_to_exo_name[node_set_id]["key"]
node_sets[geometry_type].append(exo.variables[node_set_key][:])

if use_exo_ids:
bc_description["E"] = node_set_id
else:
bc_description["E"] = len(node_sets[geometry_type])

if bc_section not in input_file.inlined.keys():
input_file[bc_section] = []
bc_description["E"] = len(node_sets[geometry_type])

if not write_topology_information:
# when working with external .exo meshes, we do not write the
Expand Down Expand Up @@ -122,12 +128,9 @@ def add_exodus_geometry_section(cubit, input_file, rel_exo_file_path):
The relative path (as seen from the yaml input file) to the exodus
file that contains the mesh.
"""
# Retrieve a list of the block IDs and the corresponding block data of the current session
element_block_ids = cubit.cubit.get_block_id_list()
element_blocks = cubit.blocks

# Iterate over all blocks and add them to the input file
for cur_block_id, cur_block_data in zip(element_block_ids, element_blocks):
for cur_block_id, cur_block_data in cubit.blocks.items():
# retrieve the name of the geometry section that this block belongs to
cur_geometry_section_key = cur_block_data[0].get_four_c_section() + " GEOMETRY"
# If the geometry section for this block does not exist yet, create it
Expand Down Expand Up @@ -238,8 +241,9 @@ def get_input_file_with_mesh(cubit):
connectivity_keys = [key for key in exo.variables.keys() if "connect" in key]
connectivity_keys.sort()
i_element = 0
for i_block, key in enumerate(connectivity_keys):
ele_type, block_dict = cubit.blocks[i_block]
for key in connectivity_keys:
key_id = int(key[7:])
ele_type, block_dict = cubit.blocks[key_id]
block_section = f"{ele_type.get_four_c_section()} ELEMENTS"
if block_section not in input_file.sections.keys():
input_file[block_section] = []
Expand Down
98 changes: 64 additions & 34 deletions src/cubitpy/cubitpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@
from cubitpy.cubit_wrapper.cubit_wrapper_host import CubitConnect


def _get_and_check_ids(name, container, id_list, given_id):
"""Perform checks for the block and node set IDs used in CubitPy."""

# Check that the IDs stored in container are the same as created with this function.
if not set(container.keys()) == set(id_list):
raise ValueError(
f"The existing {name} ids in CubitPy ({set(container.keys())}) don't match the ones in Cubit ({id_list})"
)

# Get the id of the block to create.
if given_id is None:
if len(id_list) > 0:
given_id = max(id_list) + 1
else:
given_id = 1
elif given_id in id_list:
raise ValueError(f"The provided {name} id {given_id} already exists {id_list}")
return given_id


class CubitPy(object):
"""A wrapper class with additional functionality for cubit."""

Expand Down Expand Up @@ -69,12 +89,10 @@ def __init__(self, *, cubit_exe=None, **kwargs):
# Set lists and counters for blocks and sets
self._default_cubit_variables()

self.fourc_input = FourCInput()

def _default_cubit_variables(self):
"""Set the default values for the lists and counters used in cubit."""
self.blocks = []
self.node_sets = []
self.blocks = {}
self.node_sets = {}
self.fourc_input = FourCInput()

def __getattr__(self, key, *args, **kwargs):
Expand Down Expand Up @@ -128,7 +146,14 @@ def _name_created_set(self, set_type, set_id, name, item):
self.cubit.cmd('{} {} name "{}"'.format(set_type, set_id, rename_name))

def add_element_type(
self, item, el_type, *, name=None, material=None, bc_description=None
self,
item,
el_type,
*,
name=None,
material=None,
bc_description=None,
block_id: int | None = None,
):
"""Add a block to cubit that contains the geometry in item. Also set
the element type of block.
Expand All @@ -147,6 +172,9 @@ def add_element_type(
bc_description: dict
Will be written after the material string. If this is not set, the
default values for the given element type will be used.
block_id:
Optionally the block ID can be given by the user. If this ID already exists
an error will be raised.
"""

# default values
Expand All @@ -155,19 +183,15 @@ def add_element_type(
if bc_description is None:
bc_description = {}

# Check that all blocks in cubit are created with this function.
n_blocks = len(self.blocks)
if not len(self.cubit.get_block_id_list()) == n_blocks:
raise ValueError(
"The block counter is {1}, but the number of blocks in cubit is {0}, all blocks should be created with this function!".format(
len(self.cubit.get_block_id_list()), n_blocks
)
)
# Check and get the block id for the new block.
block_id = _get_and_check_ids(
"block", self.blocks, self.cubit.get_block_id_list(), block_id
)

# Get element type of item.
geometry_type = item.get_geometry_type()

self.cubit.cmd("create block {}".format(n_blocks + 1))
self.cubit.cmd("create block {}".format(block_id))

if not isinstance(item, CubitGroup):
cubit_scheme, cubit_element_type = el_type.get_cubit_names()
Expand All @@ -181,30 +205,30 @@ def add_element_type(

self.cubit.cmd(
"block {} {} {}".format(
n_blocks + 1, geometry_type.get_cubit_string(), item.id()
block_id, geometry_type.get_cubit_string(), item.id()
)
)
self.cubit.cmd(
"block {} element type {}".format(n_blocks + 1, cubit_element_type)
"block {} element type {}".format(block_id, cubit_element_type)
)
else:
item.add_to_block(n_blocks + 1, el_type)
item.add_to_block(block_id, el_type)

self._name_created_set("block", n_blocks + 1, name, item)
self._name_created_set("block", block_id, name, item)

# If the user does not give a bc_description, load the default one.
if not bc_description:
bc_description = el_type.get_default_four_c_description()

# Add data that will be written to bc file.
self.blocks.append([el_type, material | bc_description])
self.blocks[block_id] = [el_type, material | bc_description]

def reset_blocks(self):
"""This method deletes all blocks in Cubit and resets the counter in
this object."""

# Reset the block list of this object.
self.blocks = []
self.blocks = {}

# Delete all blocks.
for block_id in self.get_block_id_list():
Expand All @@ -219,6 +243,7 @@ def add_node_set(
bc_description=None,
bc_section=None,
geometry_type=None,
node_set_id: int | None = None,
):
"""Add a node set to cubit. This node set can have a boundary
condition.
Expand All @@ -239,34 +264,33 @@ def add_node_set(
geometry_type: cupy.geometry
Directly set the geometry type, instead of obtaining it from the
given item.
node_set_id:
Optionally the node set ID can be given by the user. If this ID
already exists an error will be raised.
"""

# Check that all node sets in cubit are created with this function.
n_node_sets = len(self.node_sets)
if not len(self.cubit.get_nodeset_id_list()) == n_node_sets:
raise ValueError(
"The node set counter is {1}, but the number of node sets in cubit is {0}, all node sets should be created with this function!".format(
len(self.cubit.get_nodeset_id_list()), n_node_sets
)
)
# Check and get the node set id for the new node set.
node_set_id = _get_and_check_ids(
"nodeset", self.node_sets, self.cubit.get_nodeset_id_list(), node_set_id
)

# Get element type of item if it was not explicitly given.
if geometry_type is None:
geometry_type = item.get_geometry_type()

self.cubit.cmd("create nodeset {}".format(n_node_sets + 1))
self.cubit.cmd("create nodeset {}".format(node_set_id))
if not isinstance(item, CubitGroup):
# Add the geometries to the node set in cubit.
self.cubit.cmd(
"nodeset {} {} {}".format(
n_node_sets + 1, geometry_type.get_cubit_string(), item.id()
node_set_id, geometry_type.get_cubit_string(), item.id()
)
)
else:
# Add the group to the node set in cubit.
item.add_to_nodeset(n_node_sets + 1)
item.add_to_nodeset(node_set_id)

self._name_created_set("nodeset", n_node_sets + 1, name, item)
self._name_created_set("nodeset", node_set_id, name, item)

# Add data that will be written to bc file.
if (
Expand All @@ -282,7 +306,7 @@ def add_node_set(
bc_section = bc_type.get_dat_bc_section_header(geometry_type)
if bc_description is None:
bc_description = {}
self.node_sets.append([bc_section, bc_description, geometry_type])
self.node_sets[node_set_id] = [bc_section, bc_description, geometry_type]

def get_ids(self, geometry_type):
"""Get a list with all available ids of a certain geometry type."""
Expand Down Expand Up @@ -367,7 +391,13 @@ def dump(self, yaml_path, mesh_in_exo=False):
# create a deep copy of the input_file
input_file = self.fourc_input.copy()
# Add the node sets
add_node_sets(self, exo, input_file, write_topology_information=False)
add_node_sets(
self,
exo,
input_file,
write_topology_information=False,
use_exo_ids=True,
)
# Add the problem geometry section
rel_exo_path = os.path.relpath(exo_path, start=yaml_dir)
add_exodus_geometry_section(self, input_file, rel_exo_path)
Expand Down
52 changes: 49 additions & 3 deletions tests/input-files-ref/test_yaml_with_exo_export.4C.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ DESIGN SURF DIRICH CONDITIONS:
- null
- null
- null
E: 3
E: 17
ENTITY_TYPE: "node_set_id"
- NUMDOF: 3
ONOFF:
Expand All @@ -40,7 +40,53 @@ DESIGN SURF DIRICH CONDITIONS:
- 1
- null
- null
E: 4
E: 18
ENTITY_TYPE: "node_set_id"
DESIGN LINE NEUMANN CONDITIONS:
- NUMDOF: 3
ONOFF:
- 1
- 0
- 0
VAL:
- 1.0
- 0.0
- 0.0
FUNCT:
- null
- null
- null
E: 19
ENTITY_TYPE: "node_set_id"
- NUMDOF: 3
ONOFF:
- 1
- 0
- 0
VAL:
- 1.0
- 0.0
- 0.0
FUNCT:
- null
- null
- null
E: 20
ENTITY_TYPE: "node_set_id"
- NUMDOF: 3
ONOFF:
- 1
- 1
- 0
VAL:
- 1.0
- 0.0
- 0.0
FUNCT:
- null
- null
- null
E: 15
ENTITY_TYPE: "node_set_id"
STRUCTURE GEOMETRY:
FILE: "test_yaml_with_exo_export.exo"
Expand All @@ -49,6 +95,6 @@ STRUCTURE GEOMETRY:
- ID: 1
ELEMENT_NAME: "SOLID"
ELEMENT_DATA: "MAT 1 KINEM nonlinear"
- ID: 2
- ID: 27
ELEMENT_NAME: "SOLID"
ELEMENT_DATA: "MAT 2 KINEM nonlinear"
Loading