diff --git a/src/ansys/dyna/core/pre/dynabase.py b/src/ansys/dyna/core/pre/dynabase.py index c6dc0e317..b47c48cf2 100644 --- a/src/ansys/dyna/core/pre/dynabase.py +++ b/src/ansys/dyna/core/pre/dynabase.py @@ -14,6 +14,9 @@ from ansys.api.dyna.v0.kwprocess_pb2 import * # noqa : F403 from ansys.api.dyna.v0.kwprocess_pb2_grpc import * # noqa : F403 +# from .kwprocess_pb2 import * +# from .kwprocess_pb2_grpc import * + class Motion(Enum): VELOCITY = 0 @@ -259,6 +262,18 @@ def __init__(self, x=0, y=0, z=0): self.z = z +class BaseObj: + """Define the base object.""" + + def __init__(self): + self.type = "" + self.subtype = "" + + def get_data(self) -> List: + """Get the data of the object.""" + return None + + class DynaBase: """Contains methods for creating a general LS-DYNA keyword.""" @@ -804,6 +819,12 @@ def create_general_keyword(self, opcode, keyworddata): def add(self, obj): """Add entities to an object.""" + + if obj.type == "rigidwall_cylinder" or obj.type == "rigidwall_sphere" or obj.type == "rigidwall_planar": + data = obj.get_data() + if data != None: + model = self._parent.model + model.add_rigidwall(data) self.entities.append(obj) def set_transform(self, filename=None, idnoff=0, ideoff=0, idpoff=0, idmoff=0, idsoff=0, idfoff=0, transform=None): @@ -1788,7 +1809,7 @@ class ThermalAnalysisTimestep(Enum): VARIABLE = 1 -class ThermalAnalysis: +class ThermalAnalysis(BaseObj): """Activates thermal analysis and defines associated control parameters.""" def __init__(self): @@ -1796,6 +1817,7 @@ def __init__(self): self.defined_timestep = False self.defined_nonlinear = False self.stub = DynaBase.get_stub() + self.type = "analysis_thermal" def set_timestep(self, timestep_control=ThermalAnalysisTimestep.FIXED, initial_timestep=0): """Set time step controls for the thermal solution in a thermal only or coupled structural/thermal analysis. @@ -2591,7 +2613,7 @@ def create(self): logging.info(f"Define temperature at {type} {id}.") -class RigidwallCylinder: +class RigidwallCylinder(BaseObj): """Defines a rigid wall with a cylinder form. Parameters @@ -2617,6 +2639,7 @@ def __init__(self, tail=Point(0, 0, 0), head=Point(0, 0, 0), radius=1, length=10 self.motion = -1 self.lcid = 0 self.dir = Direction(1, 0, 0) + self.type = "rigidwall_cylinder" def set_motion(self, curve, motion=RWMotion.VELOCITY, dir=Direction(1, 0, 0)): """Set the prescribed motion.""" @@ -2625,6 +2648,21 @@ def set_motion(self, curve, motion=RWMotion.VELOCITY, dir=Direction(1, 0, 0)): self.motion = motion.value self.dir = dir + def get_data(self) -> List: + """Get the rigidwall data.""" + data = [ + self.type, + self.tail.x, + self.tail.y, + self.tail.z, + self.head.x, + self.head.y, + self.head.z, + self.radius, + self.length, + ] + return data + def create(self): """Create a rigidwall cylinder.""" parameter = [ @@ -2652,7 +2690,7 @@ def create(self): logging.info("Cylinder Rigidwall Created...") -class RigidwallSphere: +class RigidwallSphere(BaseObj): """Defines a rigid wall with a sphere form. Parameters @@ -2676,6 +2714,7 @@ def __init__(self, center=Point(0, 0, 0), orient=Point(0, 0, 0), radius=1): self.motion = -1 self.lcid = 0 self.dir = Direction(1, 0, 0) + self.type = "rigidwall_sphere" def set_motion(self, curve, motion=RWMotion.VELOCITY, dir=Direction(1, 0, 0)): """Set the prescribed motion.""" @@ -2684,6 +2723,20 @@ def set_motion(self, curve, motion=RWMotion.VELOCITY, dir=Direction(1, 0, 0)): self.motion = motion.value self.dir = dir + def get_data(self) -> List: + """Get the rigidwall data.""" + data = [ + self.type, + self.center.x, + self.center.y, + self.center.z, + self.orient.x, + self.orient.y, + self.orient.z, + self.radius, + ] + return data + def create(self): """Create a rigidwall sphere.""" parameter = [ @@ -2710,7 +2763,7 @@ def create(self): logging.info("Sphere Rigidwall Created...") -class RigidwallPlanar: +class RigidwallPlanar(BaseObj): """Defines planar rigid walls with either finite or infinite size. Parameters @@ -2730,6 +2783,12 @@ def __init__(self, tail=Point(0, 0, 0), head=Point(0, 0, 0), coulomb_friction_co self.tail = tail self.head = head self.fric = coulomb_friction_coefficient + self.type = "rigidwall_planar" + + def get_data(self) -> List: + """Get the rigidwall data.""" + data = [self.type, self.tail.x, self.tail.y, self.tail.z, self.head.x, self.head.y, self.head.z] + return data def create(self): """Create planar rigid walls.""" @@ -2753,7 +2812,7 @@ class GravityOption(Enum): DIR_Z = "Z" -class Gravity: +class Gravity(BaseObj): """Defines body force loads using global axes directions. Body force loads are due to a prescribed base acceleration or @@ -2764,6 +2823,7 @@ def __init__(self, dir=GravityOption.DIR_Z, load=Curve(x=[0, 0], y=[0, 0])): self.stub = DynaBase.get_stub() self.dir = dir.value self.load = load + self.type = "gravity" def create(self): """Define a body force.""" diff --git a/src/ansys/dyna/core/pre/dynadem.py b/src/ansys/dyna/core/pre/dynadem.py index 685eceb09..5ecc24fac 100644 --- a/src/ansys/dyna/core/pre/dynadem.py +++ b/src/ansys/dyna/core/pre/dynadem.py @@ -112,12 +112,13 @@ def save_file(self): DynaBase.save_file(self) -class DEMAnalysis: +class DEMAnalysis(BaseObj): """Activates DEM analysis and defines associated control parameters.""" def __init__(self): self.defined_des = False self.stub = DynaBase.get_stub() + self.type = "analysis_dem" def set_des( self, diff --git a/src/ansys/dyna/core/pre/dynaem.py b/src/ansys/dyna/core/pre/dynaem.py index 246200d05..38af4cf9a 100644 --- a/src/ansys/dyna/core/pre/dynaem.py +++ b/src/ansys/dyna/core/pre/dynaem.py @@ -719,7 +719,7 @@ def create(self): return self.id -class RogoCoil: +class RogoCoil(BaseObj): """Measures the total current flowing through a given section of the conductor. Parameters @@ -732,6 +732,7 @@ def __init__(self, set=None): self.stub = DynaBase.get_stub() self.set = set self.id = 0 + self.type = "rogocoil" def create(self): """Create a Rogowsky coil.""" diff --git a/src/ansys/dyna/core/pre/dynaicfd.py b/src/ansys/dyna/core/pre/dynaicfd.py index 52ed2dc6b..d02ae7cbd 100644 --- a/src/ansys/dyna/core/pre/dynaicfd.py +++ b/src/ansys/dyna/core/pre/dynaicfd.py @@ -246,7 +246,7 @@ class ICFD_CouplingDirection(Enum): TWO_WAY_WEAK_COUPLING = 3 -class ICFDAnalysis: +class ICFDAnalysis(BaseObj): """Activates an ICFD analysis and defines associated control parameters.""" def __init__(self): @@ -260,6 +260,7 @@ def __init__(self): self.defined_coupling_dem = False self.defined_mesh_adapt = False self.stub = DynaBase.get_stub() + self.type = "analysis_icfd" def set_type(self, analysis_type=ICFD_AnalysisType.TRANSIENT_ANALYSIS): """Set the type of the CFD analysis. @@ -733,7 +734,7 @@ def create(self): return ret -class MeshedVolume: +class MeshedVolume(BaseObj): """Defines the volume space to mesh. Parameters @@ -749,6 +750,7 @@ def __init__(self, surfaces): self.embeded_surf = [] self.meshsize_surf = [] self.fluid_interfaces = [] + self.type = "meshedvolume" def embed_shell(self, embeded): """Define surfaces that the mesher is to embed inside the volume mesh. diff --git a/src/ansys/dyna/core/pre/dynamech.py b/src/ansys/dyna/core/pre/dynamech.py index 12dd14743..7c25ce216 100644 --- a/src/ansys/dyna/core/pre/dynamech.py +++ b/src/ansys/dyna/core/pre/dynamech.py @@ -320,7 +320,7 @@ def save_file(self, defaultsetting=1): DynaBase.save_file(self) -class Airbag: +class Airbag(BaseObj): """Defines an airbag or control volume. Parameters @@ -377,6 +377,7 @@ def __init__( self.sidtyp = 0 else: self.sidtyp = 1 + self.type = "airbag" def create(self): """Create an airbag.""" diff --git a/src/ansys/dyna/core/pre/dynanvh.py b/src/ansys/dyna/core/pre/dynanvh.py index 653837f0c..7ac8ec691 100644 --- a/src/ansys/dyna/core/pre/dynanvh.py +++ b/src/ansys/dyna/core/pre/dynanvh.py @@ -55,12 +55,13 @@ class ResponseType(Enum): NODAL_FORCE = 3 -class FrequencyDomain: +class FrequencyDomain(BaseObj): """Provides a way of defining and solving frequency domain vibration and acoustic problems.""" def __init__(self): self.stub = DynaBase.get_stub() self.defined_frf = False + self.type = "frequency_domain" def set_frequency_response_function( self, diff --git a/src/ansys/dyna/core/pre/dynasale.py b/src/ansys/dyna/core/pre/dynasale.py index 7c85b0638..5b69ee098 100644 --- a/src/ansys/dyna/core/pre/dynasale.py +++ b/src/ansys/dyna/core/pre/dynasale.py @@ -42,7 +42,7 @@ def __init__(self, number, position, ratio): self.ratio = ratio -class StructuredMesh: +class StructuredMesh(BaseObj): """Generates a structured 2D or 3D mesh and invokes the S-ALE solver.""" def __init__(self, control_points_x, control_points_y, control_points_z): @@ -54,6 +54,7 @@ def __init__(self, control_points_x, control_points_y, control_points_z): self.refine_factor_y = 1 self.refine_factor_z = 1 self.fillings = [] + self.type = "structured_mesh" def fill( self, diff --git a/src/ansys/dyna/core/pre/dynasolution.py b/src/ansys/dyna/core/pre/dynasolution.py index 62ec0e71e..563d72df8 100644 --- a/src/ansys/dyna/core/pre/dynasolution.py +++ b/src/ansys/dyna/core/pre/dynasolution.py @@ -19,6 +19,10 @@ from ansys.dyna.core.pre.model import Model +# from .kwprocess_pb2 import * +# from .kwprocess_pb2_grpc import * + + # from .launcher import * # noqa : F403 CHUNK_SIZE = 1024 * 1024 diff --git a/src/ansys/dyna/core/pre/graphics/graphics.py b/src/ansys/dyna/core/pre/graphics/graphics.py index 6d0b55a64..1e030b040 100644 --- a/src/ansys/dyna/core/pre/graphics/graphics.py +++ b/src/ansys/dyna/core/pre/graphics/graphics.py @@ -18,6 +18,9 @@ class DisplayMeshType(enum.IntEnum): FACE = 0 BEAM = 1 + CYLINDER = 2 + SPHERE = 3 + PLANAR = 4 class ColorByType(enum.IntEnum): @@ -173,7 +176,7 @@ def __init__(self, model: pre.Model, use_trame: bool = False): """Initialize graphics.""" self._model = model self._display_data = {} - self._display_spline_data = {} + self._display_entity_data = {} self._plotter = None self._picker = None self._color_by_type = ColorByType.ZONE @@ -223,6 +226,15 @@ def __update_display_data(self): self._display_data[part_id] = data self._init_velocity_data = self._model.get_init_velocity() self._bdy_spc = self._model.get_bdy_spc() + # rigidwall + self._display_entity_data.clear() + num = len(self._model._rigidwall) + for entity_id in range(1, num + 1): + data = {} + disp_mesh_data: list[_DisplayMesh] = [self.__get_entity_display_mesh_object(entity_id)] + if len(disp_mesh_data) > 0: + data["faces"] = disp_mesh_data + self._display_entity_data[entity_id] = data # self._model._sync_up_model() @@ -259,6 +271,44 @@ def __get_face_display_mesh_object(self, part_id: int): ) return disp_mesh + def __get_entity_display_mesh_object(self, entity_id: int): + """Display the faces in an object. + + Parameters + ---------- + entity_id : int + ID of the entity to show the edges on. + + Returns + ------- + _DisplayMesh + Displayed mesh. + """ + index = entity_id - 1 + rw = self._model._rigidwall[index] + type = rw[0] + if type == "rigidwall_cylinder": + meshtype = DisplayMeshType.CYLINDER + mesh = pv.Cylinder( + center=[rw[1], rw[2], rw[3]], direction=[rw[4], rw[5], rw[6]], radius=rw[7], height=rw[8] + ) + elif type == "rigidwall_sphere": + meshtype = DisplayMeshType.SPHERE + mesh = pv.Sphere(center=[rw[1], rw[2], rw[3]], direction=[rw[4], rw[5], rw[6]], radius=rw[7]) + elif type == "rigidwall_planar": + meshtype = DisplayMeshType.PLANAR + dir = [rw[4] - rw[1], rw[5] - rw[2], rw[6] - rw[3]] + mesh = pv.Plane(center=[rw[1], rw[2], rw[3]], direction=dir, i_size=1000, j_size=1000) + + disp_mesh = _DisplayMesh( + type=meshtype, + part_id=entity_id, + graphics=self, + model=self._model, + mesh=mesh, + ) + return disp_mesh + def __call__( self, parts: List = None, @@ -519,6 +569,13 @@ def __draw_parts(self, parts: List = [], update: bool = False, spline: bool = Fa for key, disp_mesh_data in data.items() for disp_mesh in disp_mesh_data ] + + [ + disp_mesh.add_to_plotter(self._plotter) + for entity_id, data in self._display_entity_data.items() + for key, disp_mesh_data in data.items() + for disp_mesh in disp_mesh_data + ] if self._sphinx_build == False: self._colorByTypeBt = self._plotter.add_checkbox_button_widget( self.__show_bdy_spc_callback, @@ -739,8 +796,9 @@ def __init__( part_id: int, graphics: Graphics, model: pre.Model, - vertices: np.array, - facet_list: np.array, + vertices: np.array = None, + facet_list: np.array = None, + mesh=None, part_name: str = "", ): """Initialize the parameters to display.""" @@ -750,6 +808,7 @@ def __init__( self._model = model self._vertices = vertices self._facet_list = facet_list + self._mesh = mesh self._poly_data = None self._actor = None self._part_name = part_name @@ -795,15 +854,21 @@ def add_to_plotter(self, plotter: Plotter): self._actor = plotter.add_mesh( self._poly_data, show_edges=True, scalars="colors", rgb=True, pickable=True ) + elif ( + self._type is DisplayMeshType.CYLINDER + or self._type is DisplayMeshType.SPHERE + or self._type is DisplayMeshType.PLANAR + ): + surf = self._mesh + fcolor = np.array(self.get_face_color()) + colors = np.tile(fcolor, (surf.n_faces, 1)) + surf["colors"] = colors + surf.disp_mesh = self + self._poly_data = surf + self._actor = plotter.add_mesh( + self._poly_data, show_edges=True, scalars="colors", rgb=True, pickable=True + ) return - lines = [] - for line in self._facet_list: - coord = self._vertices[line[1]] - lines.append(coord) - coord = self._vertices[line[2]] - lines.append(coord) - self._poly_data = np.array(lines) - self._actor = plotter.add_lines(self._poly_data, color="purple", width=8) def get_face_color(self): """Get the colors of faces. diff --git a/src/ansys/dyna/core/pre/model.py b/src/ansys/dyna/core/pre/model.py index 40461604e..f47163e91 100644 --- a/src/ansys/dyna/core/pre/model.py +++ b/src/ansys/dyna/core/pre/model.py @@ -5,6 +5,8 @@ from ansys.api.dyna.v0.kwprocess_pb2 import * # noqa : F403 from ansys.api.dyna.v0.kwprocess_pb2_grpc import * # noqa : F403 +# from .kwprocess_pb2 import * +# from .kwprocess_pb2_grpc import * from ansys.dyna.core.pre.part import Part @@ -20,6 +22,7 @@ def __init__(self, stub): self._nodes = [] self._bdy_spc: List = [] self._init_velocity: List = [] + self._rigidwall = [] # self._freeze() def add_bdy_spc(self, nodes): @@ -30,6 +33,10 @@ def add_init_velocity(self, nodes): """Add initial velocity nodes.""" self._init_velocity.append(nodes) + def add_rigidwall(self, data: List): + """Add rigidwall data.""" + self._rigidwall.append(data) + def get_solid_elements(self) -> List: """Get the solid elements. @@ -113,6 +120,23 @@ def get_bdy_spc(self) -> List: nlist = [coord[i : i + num] for i in range(0, len(coord), num)] return nlist + def get_rigidwall(self, id): + """Get rigidwall data.""" + + data = self._rigidwall(id - 1) + return data + data = self.stub.GetRigidWall(GetRigidWallRequest(id=id)) + geomtype = data.geomtype + param = data.parameter + rigidwall = [geomtype] + if geomtype == 3: + num = 8 + elif geomtype == 4: + num = 7 + for i in range(num): + rigidwall.append(param[i]) + return rigidwall + def _sync_up_model(self): """Synchronize the client model with the server model. @@ -189,6 +213,12 @@ def _sync_up_model(self): for pid, conn in beam_dict.items(): self._parts.append(Part(self, conn[0][1], conn[0][0], "BEAM", conn[1])) return + rigid_num = self.stub.GetNum(GetNumRequest(type="rigidwall")) + num = rigid_num.num + for i in range(1, num + 1): + rigid = self.get_rigidwall(id=i) + self._rigidwall.append(rigid) + return @property def parts(self) -> List[Part]: diff --git a/src/ansys/dyna/core/solver/dynasolver.py b/src/ansys/dyna/core/solver/dynasolver.py index 503da5b23..e702beace 100644 --- a/src/ansys/dyna/core/solver/dynasolver.py +++ b/src/ansys/dyna/core/solver/dynasolver.py @@ -350,13 +350,13 @@ def quit(self): command and continues running. """ self.logger.debug("quit") - request = dynasolver_pb2.QuitServer() - # ALWAYS returns ACK, so don't bother checking - self.stub.quit_server(request) if self.pim_client is not None: self.pim_client.close() if self.remote_instance is not None: self.remote_instance.delete() + request = dynasolver_pb2.QuitServer() + # ALWAYS returns ACK, so don't bother checking + self.stub.quit_server(request) return def resume(self, cycle=None, time=None):