From f92fbe9c496d12fa38753ecac40d0c78bda193b0 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:17:13 -0800 Subject: [PATCH 01/16] missing pytest in automated test --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e3fdf3..ab39928 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,6 +29,7 @@ jobs: python -m pip install --upgrade pip python -m pip install flit flit install --deps production + pip install pytest - name: Test with pytest run: | pytest . From c6f3b00c00e1234c098c41b4d00da6f1ff6629d7 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:43:29 -0800 Subject: [PATCH 02/16] removed unused func --- src/tests/test_credentials.py | 40 ----------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/tests/test_credentials.py diff --git a/src/tests/test_credentials.py b/src/tests/test_credentials.py deleted file mode 100644 index bbda24b..0000000 --- a/src/tests/test_credentials.py +++ /dev/null @@ -1,40 +0,0 @@ -import pytest - -from unittest import mock -import getpass -import sys - -from ak_sap.utils.credentials import getpwd - -@pytest.fixture(autouse=True) -def mock_sys_platform(request): - """Mock sys.platform to 'win32' for Windows tests""" - if request.node.get_closest_marker("windows"): - with mock.patch.object(sys, 'platform', "win32"): - yield - else: - yield - -@pytest.fixture(autouse=True) -def mock_keyring(request): - """Mock keyring module for Windows tests""" - if request.node.get_closest_marker("windows"): - with mock.patch("credentials.keyring") as mock_keyring: - yield mock_keyring - else: - yield None - -def test_getpwd_linux(monkeypatch): - """Test getpwd function on Linux""" - item = "example" - username = "testuser" - expected_password = "testpass" - - # Mock the getpass.getpass function - monkeypatch.setattr(getpass, 'getpass', lambda prompt: expected_password) - - # Call the function - password = getpwd(item, username) - - # Assert the password - assert password == expected_password From 71efdd15eff8cf802fe6e8a679b4653a49f2da38 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:53:41 -0800 Subject: [PATCH 03/16] separated gui installation to be optional --- README.md | 15 ++++++++++++++- pyproject.toml | 5 ++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b7834e4..a6d5b6d 100644 --- a/README.md +++ b/README.md @@ -81,15 +81,28 @@ Clone repo and Install with flit git clone https://github.com/rpakishore/ak_sap.git cd ak_sap pip install flit -flit install --deps production ``` +- If you want just the base package: + + ```bash + flit install --deps production + ``` + +- Alternatively, if you also want to include the optional streamlit gui: + + ```bash + flit install --deps production --extras gui + ``` + ##### 2.2.1.3. Install from Pypi release ```bash pip install ak_sap ``` +Note: The Pypi version does not ship with the optional streamlit gui + #### 2.2.2. Development Download the git and install via flit diff --git a/pyproject.toml b/pyproject.toml index 4a2ef0c..0673776 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,10 @@ dev = [ "ipywidgets", "pandasgui", "line-profiler", - "pytest==7.4.3", + "pytest==7.4.3" +] + +gui = [ "streamlit==1.31.0", "hilti_profis==0.0.3" ] From 144a4aaa642ae7c6a90809624ea2b8016068c7b2 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:56:53 -0800 Subject: [PATCH 04/16] separated test to be optional for automated testing --- .github/workflows/test.yml | 3 +-- pyproject.toml | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ab39928..cb94c70 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,8 +28,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flit - flit install --deps production - pip install pytest + flit install --deps production --extras test - name: Test with pytest run: | pytest . diff --git a/pyproject.toml b/pyproject.toml index 0673776..31ed417 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,7 @@ dev = [ "ipykernel", "ipywidgets", "pandasgui", - "line-profiler", - "pytest==7.4.3" + "line-profiler" ] gui = [ @@ -33,6 +32,10 @@ gui = [ "hilti_profis==0.0.3" ] +test = [ + "pytest==7.4.3" +] + [project.urls] Home = "https://github.com/rpakishore/ak_sap" From 7b186f3313dbe4d9a946e7cfbc21fc473fa9c74b Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 11:14:44 -0800 Subject: [PATCH 05/16] Element -> Object Changed `Element` references to `Object` references to be consistent with SAP nomenclature --- documentation/Layout.md | 8 ++++---- documentation/Usage.ipynb | 8 ++++---- pages/Export/hilti_export.py | 4 ++-- src/ak_sap/Object/__init__.py | 2 +- src/ak_sap/Object/frame.py | 4 ++-- src/ak_sap/Object/helper.py | 8 ++++---- src/ak_sap/Object/{elem.py => obj.py} | 2 +- src/ak_sap/Object/point.py | 4 ++-- src/ak_sap/wrapper.py | 4 ++-- 9 files changed, 22 insertions(+), 22 deletions(-) rename src/ak_sap/Object/{elem.py => obj.py} (93%) diff --git a/documentation/Layout.md b/documentation/Layout.md index aaed9cc..3a0a3cc 100644 --- a/documentation/Layout.md +++ b/documentation/Layout.md @@ -65,14 +65,14 @@ Manipulate Point Elements Usage Examples ```python -points = sap.Element.Point +points = sap.Object.Point len(points) #list number of points in model points.add_by_coord((1,2,3)) #Add point to model points.is_selected(name='1') #Check if point is selected points.selected() #Yields selected points points.all() #Lists all defined points points.rename(old_name='1', new_name='1_1') #Rename point -points.check_element_legal(name='1') #Asserts point's existance +points.check_obj_legal(name='1') #Asserts point's existance points.delete(name='1') #Delete point ``` @@ -83,13 +83,13 @@ Manipulate Frame Elements Usage Examples ```python -frames = sap.Element.Frame +frames = sap.Object.Frame len(frames) #list number of frames in model frames.is_selected(name='1') #Check if frame is selected frames.selected() #Yields selected frames frames.all() #Lists all defined frames frames.rename(old_name='1', new_name='1_1') #Rename frame -frames.check_element_legal(name='1') #Asserts frame's existance +frames.check_obj_legal(name='1') #Asserts frame's existance frames.get_section(frame_name='1') #Get the assigned Section name frames.get_points(frame_name='1') #Get points connected to frame frames.delete(name='1') #Delete frame diff --git a/documentation/Usage.ipynb b/documentation/Usage.ipynb index 54421cd..a22d184 100644 --- a/documentation/Usage.ipynb +++ b/documentation/Usage.ipynb @@ -106,14 +106,14 @@ "metadata": {}, "outputs": [], "source": [ - "points = sap.Element.Point\n", + "points = sap.Object.Point\n", "len(points) #list number of points in model\n", "points.add_by_coord((1,2,3)) #Add point to model\n", "points.is_selected(name='1') #Check if point is selected\n", "points.selected() #Yields selected points\n", "points.all() #Lists all defined points\n", "points.rename(old_name='1', new_name='1_1') #Rename point\n", - "points.check_element_legal(name='1') #Asserts point's existance\n", + "points.check_obj_legal(name='1') #Asserts point's existance\n", "points.delete(name='1') #Delete point" ] }, @@ -130,13 +130,13 @@ "metadata": {}, "outputs": [], "source": [ - "frames = sap.Element.Frame\n", + "frames = sap.Object.Frame\n", "len(frames) #list number of frames in model\n", "frames.is_selected(name='1') #Check if frame is selected\n", "frames.selected() #Yields selected frames\n", "frames.all() #Lists all defined frames\n", "frames.rename(old_name='1', new_name='1_1') #Rename frame\n", - "frames.check_element_legal(name='1') #Asserts frame's existance\n", + "frames.check_obj_legal(name='1') #Asserts frame's existance\n", "frames.get_section(frame_name='1') #Get the assigned Section name\n", "frames.get_points(frame_name='1') #Get points connected to frame\n", "frames.delete(name='1') #Delete frame\n", diff --git a/pages/Export/hilti_export.py b/pages/Export/hilti_export.py index 03c5ced..4c44890 100644 --- a/pages/Export/hilti_export.py +++ b/pages/Export/hilti_export.py @@ -22,7 +22,7 @@ def main(): def _hilti_extract_values(): try: - st.session_state['_hilti_values_extracted'] = sap.Element.Point.selected().__next__() + st.session_state['_hilti_values_extracted'] = sap.Object.Point.selected().__next__() except StopIteration: st.session_state['_hilti_values_extracted'] = None st.warning('Make sure the node is selected.') @@ -35,7 +35,7 @@ def load_hilti_class(basefile): if st.session_state['_hilti_values_extracted']: try: - selected_section = sap.Element.Frame.get_section(frame_name=sap.Element.Frame.selected().__next__()) + selected_section = sap.Object.Frame.get_section(frame_name=sap.Object.Frame.selected().__next__()) except StopIteration: selected_section = None diff --git a/src/ak_sap/Object/__init__.py b/src/ak_sap/Object/__init__.py index 6289242..662cfb5 100644 --- a/src/ak_sap/Object/__init__.py +++ b/src/ak_sap/Object/__init__.py @@ -1 +1 @@ -from .elem import Element \ No newline at end of file +from .obj import Object \ No newline at end of file diff --git a/src/ak_sap/Object/frame.py b/src/ak_sap/Object/frame.py index f0ba9b2..7c2b0dc 100644 --- a/src/ak_sap/Object/frame.py +++ b/src/ak_sap/Object/frame.py @@ -1,8 +1,8 @@ from ak_sap.utils import log from ak_sap.utils.decorators import smooth_sap_do -from .helper import MasterElem +from .helper import MasterObj -class Frame(MasterElem): +class Frame(MasterObj): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.FrameObj) diff --git a/src/ak_sap/Object/helper.py b/src/ak_sap/Object/helper.py index ed7a286..4c18030 100644 --- a/src/ak_sap/Object/helper.py +++ b/src/ak_sap/Object/helper.py @@ -3,7 +3,7 @@ from ak_sap.utils.logger import log from ak_sap.utils.decorators import smooth_sap_do -class MasterElem: +class MasterObj: def __init__(self, mySapObject, ElemObj) -> None: self.mySapObject = mySapObject self.SapModel = self.mySapObject.SapModel @@ -35,7 +35,7 @@ def selected(self) -> Generator[str, Any, None]: yield elem def is_selected(self, name:str) -> bool: - self.check_element_legal(name) + self.check_obj_legal(name) return self.ElemObj.GetSelected(name)[0] @smooth_sap_do @@ -47,10 +47,10 @@ def all(self) -> tuple[str]: @smooth_sap_do def rename(self, old_name: str, new_name: str): """Change the name of the element""" - self.check_element_legal(name=old_name) + self.check_obj_legal(name=old_name) return self.ElemObj.ChangeName(old_name, new_name) - def check_element_legal(self, name: str): + def check_obj_legal(self, name: str): """Confirms specified element exists in the model""" assert name in self.all(), f'`{name}` not found in the current list of elements: {self.all()}' diff --git a/src/ak_sap/Object/elem.py b/src/ak_sap/Object/obj.py similarity index 93% rename from src/ak_sap/Object/elem.py rename to src/ak_sap/Object/obj.py index c1ae5c0..6db0121 100644 --- a/src/ak_sap/Object/elem.py +++ b/src/ak_sap/Object/obj.py @@ -1,7 +1,7 @@ from .point import Point from .frame import Frame -class Element: +class Object: def __init__(self, mySapObject) -> None: self.Point = Point(mySapObject=mySapObject) self.Frame = Frame(mySapObject=mySapObject) \ No newline at end of file diff --git a/src/ak_sap/Object/point.py b/src/ak_sap/Object/point.py index 79c47a1..c313d19 100644 --- a/src/ak_sap/Object/point.py +++ b/src/ak_sap/Object/point.py @@ -1,8 +1,8 @@ from ak_sap.utils import log from ak_sap.utils.decorators import smooth_sap_do -from .helper import MasterElem +from .helper import MasterObj -class Point(MasterElem): +class Point(MasterObj): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.PointObj) diff --git a/src/ak_sap/wrapper.py b/src/ak_sap/wrapper.py index fff770a..2a0764d 100644 --- a/src/ak_sap/wrapper.py +++ b/src/ak_sap/wrapper.py @@ -7,7 +7,7 @@ from ak_sap.Loads import Load from ak_sap.Material.material import Material from ak_sap.Model import Model -from ak_sap.Object import Element +from ak_sap.Object import Object from ak_sap.Results import Results from ak_sap.utils.logger import log @@ -20,7 +20,7 @@ def __init__(self, attach_to_exist: bool = True, program_path: str|Path|None = N #Attach submodules and functions self.Model = Model(mySapObject=self.mySapObject) - self.Element = Element(mySapObject=self.mySapObject) + self.Object = Object(mySapObject=self.mySapObject) self.Table = Table(mySapObject=self.mySapObject, Model=self.Model) self.Load = Load(mySapObject=self.mySapObject) self.Results = Results(mySapObject=self.mySapObject) From fba0b65097130dd8cf914d6f05ad123413cdf800 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 11:52:40 -0800 Subject: [PATCH 06/16] test --- documentation/Usage.ipynb | 1 + src/ak_sap/Object/frame.py | 11 ++++-- src/tests/utils/test_decorators.py | 54 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/tests/utils/test_decorators.py diff --git a/documentation/Usage.ipynb b/documentation/Usage.ipynb index a22d184..cab0f3b 100644 --- a/documentation/Usage.ipynb +++ b/documentation/Usage.ipynb @@ -141,6 +141,7 @@ "frames.get_points(frame_name='1') #Get points connected to frame\n", "frames.delete(name='1') #Delete frame\n", "\n", + "# Get frame properties\n", "frames.Prop.rename(old_name=\"FSEC1\", new_name=\"MySection\") #Rename frame property\n", "frames.Prop.total() #Total # of defined frame properties" ] diff --git a/src/ak_sap/Object/frame.py b/src/ak_sap/Object/frame.py index 7c2b0dc..7e9636b 100644 --- a/src/ak_sap/Object/frame.py +++ b/src/ak_sap/Object/frame.py @@ -1,3 +1,5 @@ +from typing import Literal + from ak_sap.utils import log from ak_sap.utils.decorators import smooth_sap_do from .helper import MasterObj @@ -6,19 +8,24 @@ class Frame(MasterObj): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.FrameObj) + self.EditFrame = mySapObject.SapModel.EditFrame self.Prop = Prop(mySapObject=mySapObject) @smooth_sap_do def get_section(self, frame_name: str) -> str: - self.check_element_legal(name=frame_name) + self.check_obj_legal(name=frame_name) _ret = self.ElemObj.GetSection(frame_name) return (_ret[0], _ret[-1]) # type: ignore @smooth_sap_do def get_points(self, frame_name: str) -> tuple[str]: """retrieves the names of the point objects at each end of a specified frame object.""" - #self.check_element_legal(name=frame_name) + #self.check_obj_legal(name=frame_name) return self.ElemObj.GetPoints(frame_name) + + @smooth_sap_do + def divide_by_distance(self, frame_name: str, dist: float, Iend: bool=True, num_divisions: int=1): + return self.EditFrame.DivideAtDistance(frame_name, dist, Iend) class Prop: def __init__(self, mySapObject) -> None: diff --git a/src/tests/utils/test_decorators.py b/src/tests/utils/test_decorators.py new file mode 100644 index 0000000..2af8d2b --- /dev/null +++ b/src/tests/utils/test_decorators.py @@ -0,0 +1,54 @@ +from ak_sap.utils.decorators import smooth_sap_do + + +def test_smooth_sap_do(): + + @smooth_sap_do + def return_failed_list(): + return [1,2,3,1] + assert return_failed_list() is None + + @smooth_sap_do + def return_successful_list(): + return [1,2,3,0] + assert return_successful_list() == [1,2,3] + + @smooth_sap_do + def return_successful_tuple(): + return [(1,2,3),0] + assert return_successful_tuple() == (1,2,3) + + @smooth_sap_do + def return_failed_tuple(): + return [(1,2,3),1] + assert return_failed_tuple() is None + + @smooth_sap_do + def return_successful_str(): + return ['alpha',0] + assert return_successful_str() == 'alpha' + + @smooth_sap_do + def return_failed_str(): + return ['alpha',1] + assert return_failed_str() is None + + @smooth_sap_do + def return_successful_float(): + return [3.14,0] + assert return_successful_float() == 3.14 + + @smooth_sap_do + def return_failed_float(): + return [3.14,1] + assert return_failed_float() is None + + @smooth_sap_do + def return_success(): + return 0 + assert return_success() == 0 + + @smooth_sap_do + def return_fail(): + return 1 + assert return_fail() is None \ No newline at end of file From e9b6dca648471cfe567b7d33dcba76c54b711505 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:13:04 -0800 Subject: [PATCH 07/16] frame edit options added --- documentation/Layout.md | 15 ++++++-- documentation/Usage.ipynb | 12 +++++-- src/ak_sap/Object/frame.py | 70 ++++++++++++++++++++++++++++++++------ 3 files changed, 82 insertions(+), 15 deletions(-) diff --git a/documentation/Layout.md b/documentation/Layout.md index 3a0a3cc..6fb4715 100644 --- a/documentation/Layout.md +++ b/documentation/Layout.md @@ -90,10 +90,19 @@ frames.selected() #Yields selected frames frames.all() #Lists all defined frames frames.rename(old_name='1', new_name='1_1') #Rename frame frames.check_obj_legal(name='1') #Asserts frame's existance -frames.get_section(frame_name='1') #Get the assigned Section name -frames.get_points(frame_name='1') #Get points connected to frame -frames.delete(name='1') #Delete frame +frames.get_section(name='1') #Get the assigned Section name +frames.get_points(name='1') #Get points connected to frame +#Manipulation +frames.delete(name='1') #Delete frame +frames.divide_by_distance(name='1', + dist=0.5,Iend=True) #Divide frame by distance +frames.divide_by_intersection(name='2') #Divide at selected intersections +frames.divide_by_ratio(name='3',ratio=0.3)#Divide at selected ratio +frames.join('2','3') #Join Colinear frames +frames.change_points(name='1', point1='1', point2='3') #Change connected points of frame + +# Get frame properties frames.Prop.rename(old_name="FSEC1", new_name="MySection") #Rename frame property frames.Prop.total() #Total # of defined frame properties ``` diff --git a/documentation/Usage.ipynb b/documentation/Usage.ipynb index cab0f3b..642b0e9 100644 --- a/documentation/Usage.ipynb +++ b/documentation/Usage.ipynb @@ -137,9 +137,17 @@ "frames.all() #Lists all defined frames\n", "frames.rename(old_name='1', new_name='1_1') #Rename frame\n", "frames.check_obj_legal(name='1') #Asserts frame's existance\n", - "frames.get_section(frame_name='1') #Get the assigned Section name\n", - "frames.get_points(frame_name='1') #Get points connected to frame\n", + "frames.get_section(name='1') #Get the assigned Section name\n", + "frames.get_points(name='1') #Get points connected to frame\n", + "\n", + "#Manipulation\n", "frames.delete(name='1') #Delete frame\n", + "frames.divide_by_distance(name='1',\n", + " dist=0.5,Iend=True) #Divide frame by distance\n", + "frames.divide_by_intersection(name='2') #Divide at selected intersections\n", + "frames.divide_by_ratio(name='3',ratio=0.3)#Divide at selected ratio\n", + "frames.join('2','3') #Join Colinear frames\n", + "frames.change_points(name='1', point1='1', point2='3') #Change connected points of frame\n", "\n", "# Get frame properties\n", "frames.Prop.rename(old_name=\"FSEC1\", new_name=\"MySection\") #Rename frame property\n", diff --git a/src/ak_sap/Object/frame.py b/src/ak_sap/Object/frame.py index 7e9636b..91dc45b 100644 --- a/src/ak_sap/Object/frame.py +++ b/src/ak_sap/Object/frame.py @@ -1,5 +1,3 @@ -from typing import Literal - from ak_sap.utils import log from ak_sap.utils.decorators import smooth_sap_do from .helper import MasterObj @@ -12,21 +10,73 @@ def __init__(self, mySapObject) -> None: self.Prop = Prop(mySapObject=mySapObject) @smooth_sap_do - def get_section(self, frame_name: str) -> str: - self.check_obj_legal(name=frame_name) - _ret = self.ElemObj.GetSection(frame_name) + def get_section(self, name: str) -> str: + self.check_obj_legal(name=name) + _ret = self.ElemObj.GetSection(name) return (_ret[0], _ret[-1]) # type: ignore @smooth_sap_do - def get_points(self, frame_name: str) -> tuple[str]: + def get_points(self, name: str) -> tuple[str]: """retrieves the names of the point objects at each end of a specified frame object.""" - #self.check_obj_legal(name=frame_name) - return self.ElemObj.GetPoints(frame_name) + #self.check_obj_legal(name=name) + return self.ElemObj.GetPoints(name) + + @smooth_sap_do + def divide_by_distance(self, name: str, dist: float, Iend: bool=True) -> tuple[str]: + """divides straight frame objects into two objects at a location defined by the Dist and IEnd items. + Curved frame objects are not divided. + """ + return self.EditFrame.DivideAtDistance(name, dist, Iend) + + @smooth_sap_do + def divide_by_intersection(self, name: str) -> tuple[str]: + """divides straight frame objects at intersections with selected point objects, line objects, area edges and solid edges. + Curved frame objects are not divided. + + Args: + name (str): Frame Name + + Returns: + tuple[str]: array that includes the names of the new frame objects. + """ + return self.EditFrame.DivideAtIntersections(name)[1:] @smooth_sap_do - def divide_by_distance(self, frame_name: str, dist: float, Iend: bool=True, num_divisions: int=1): - return self.EditFrame.DivideAtDistance(frame_name, dist, Iend) + def divide_by_ratio(self,name: str, ratio: float, num_frames: int=1) -> tuple[str]: + """divides straight frame objects based on a specified Last/First length ratio. + Curved frame objects are not divided. + + Args: + name (str): name of an existing straight frame object. + ratio (float): Last/First length ratio for the new frame objects. + num_frames (int, optional): frame object is divided into this number of new objects. Defaults to 1. + Returns: + tuple[str]: array that includes the names of the new frame objects. + """ + return self.EditFrame.DivideByRatio(name, num_frames, ratio) + + @smooth_sap_do + def join(self, frame1: str, frame2: str) -> bool: + """joins two straight frame objects that have a common end point and are colinear. + + Args: + frame1 (str): name of an existing frame object to be joined. The new, joined frame object keeps this name. + frame2 (_type_): name of an existing frame object to be joined. + """ + return self.EditFrame.Join(frame1, frame2) + + @smooth_sap_do + def change_points(self, name: str, point1: str, point2: str) -> bool: + """modifies the connectivity of a frame object. + + Args: + name (str): name of an existing frame object + point1 (str): name of the point object at the I-End of the frame object. + point2 (str): name of the point object at the J-End of the frame object. + """ + return self.EditFrame.ChangeConnectivity(name, point1, point2) + class Prop: def __init__(self, mySapObject) -> None: self.SapModel=mySapObject.SapModel From c9c68a7eaaca5b4b4a45a7286eb14441921e998f Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:05:20 -0800 Subject: [PATCH 08/16] EditPoint added --- src/ak_sap/Object/point.py | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/ak_sap/Object/point.py b/src/ak_sap/Object/point.py index c313d19..22659c8 100644 --- a/src/ak_sap/Object/point.py +++ b/src/ak_sap/Object/point.py @@ -1,3 +1,5 @@ +from typing import Literal + from ak_sap.utils import log from ak_sap.utils.decorators import smooth_sap_do from .helper import MasterObj @@ -5,6 +7,8 @@ class Point(MasterObj): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.PointObj) + self.EditPoint = mySapObject.SapModel.EditPoint + self.PointObj = mySapObject.SapModel.PointObj @smooth_sap_do def add_by_coord(self, point: tuple[float, float, float], name: str='', coord_sys: str = 'Global') -> str: @@ -15,3 +19,58 @@ def add_by_coord(self, point: tuple[float, float, float], name: str='', coord_sy coord_sys (str, optional): Name of coordinate system. Defaults to 'Global'. """ return self.ElemObj.AddCartesian(*point, '', name, coord_sys) + + @smooth_sap_do + def align(self, axis: Literal['X', 'Y', 'Z'], ordinate: float) -> tuple: + """aligns selected point objects. + + Args: + axis (Literal['X', 'Y', 'Z']): Align points to this ordinate in present coordinate system + ordinate (float): The X, Y or Z ordinate that applies + + Returns: + tuple: (number of point objects that are in a new location after the alignment is complete., + array of the name of each point object that is in a new location after the alignment is complete.) + """ + _axis = ['X','Y','Z'].index(axis.upper().strip()) + 1 + return self.EditPoint.Align(_axis, ordinate) + + @smooth_sap_do + def select(self, name: str) -> bool: + return self.PointObj.SetSelected(name, True) + + @smooth_sap_do + def deselect(self, name: str) -> bool: + return self.PointObj.SetSelected(name, False) + + @smooth_sap_do + def deselect_all(self) -> bool: + return self.PointObj.ClearSelection() + + @smooth_sap_do + def merge(self, tolerance: float) -> tuple: + """merges selected point objects that are within a specified distance of one another. + + Args: + tolerance (float): Point objects within this distance of one another are merged into one point object. + + Returns: + tuple: (number of the selected point objects that still exist after the merge is complete., + array of the name of each selected point object that still exists after the merge is complete.) + """ + return self.EditPoint.Merge(tolerance) + + @smooth_sap_do + def change_coordinate(self, name: str, x: float, y: float, z: float) -> bool: + """changes the coordinates of a specified point object. + + Args: + name (str): name of an existing point object. + x (float): new x coordinate. + y (float): new y coordinate. + z (float): new z coordinate. + + Returns: + bool: Success + """ + return self.EditPoint.ChangeCoordinates_1(name, x, y, z) \ No newline at end of file From 6f1397945c02fe27998daca6614117873c7a2ae0 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:15:13 -0800 Subject: [PATCH 09/16] refactor to hide repeated `SapModel` from user --- src/ak_sap/Object/frame.py | 18 +++++++++--------- src/ak_sap/Object/helper.py | 20 ++++++++++---------- src/ak_sap/Object/obj.py | 17 +++++++++++++++-- src/ak_sap/Object/point.py | 16 ++++++++-------- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/ak_sap/Object/frame.py b/src/ak_sap/Object/frame.py index 91dc45b..f9cdee3 100644 --- a/src/ak_sap/Object/frame.py +++ b/src/ak_sap/Object/frame.py @@ -6,7 +6,7 @@ class Frame(MasterObj): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.FrameObj) - self.EditFrame = mySapObject.SapModel.EditFrame + self.__EditFrame = mySapObject.SapModel.EditFrame self.Prop = Prop(mySapObject=mySapObject) @smooth_sap_do @@ -26,7 +26,7 @@ def divide_by_distance(self, name: str, dist: float, Iend: bool=True) -> tuple[s """divides straight frame objects into two objects at a location defined by the Dist and IEnd items. Curved frame objects are not divided. """ - return self.EditFrame.DivideAtDistance(name, dist, Iend) + return self.__EditFrame.DivideAtDistance(name, dist, Iend) @smooth_sap_do def divide_by_intersection(self, name: str) -> tuple[str]: @@ -39,7 +39,7 @@ def divide_by_intersection(self, name: str) -> tuple[str]: Returns: tuple[str]: array that includes the names of the new frame objects. """ - return self.EditFrame.DivideAtIntersections(name)[1:] + return self.__EditFrame.DivideAtIntersections(name)[1:] @smooth_sap_do def divide_by_ratio(self,name: str, ratio: float, num_frames: int=1) -> tuple[str]: @@ -54,7 +54,7 @@ def divide_by_ratio(self,name: str, ratio: float, num_frames: int=1) -> tuple[st Returns: tuple[str]: array that includes the names of the new frame objects. """ - return self.EditFrame.DivideByRatio(name, num_frames, ratio) + return self.__EditFrame.DivideByRatio(name, num_frames, ratio) @smooth_sap_do def join(self, frame1: str, frame2: str) -> bool: @@ -64,7 +64,7 @@ def join(self, frame1: str, frame2: str) -> bool: frame1 (str): name of an existing frame object to be joined. The new, joined frame object keeps this name. frame2 (_type_): name of an existing frame object to be joined. """ - return self.EditFrame.Join(frame1, frame2) + return self.__EditFrame.Join(frame1, frame2) @smooth_sap_do def change_points(self, name: str, point1: str, point2: str) -> bool: @@ -75,11 +75,11 @@ def change_points(self, name: str, point1: str, point2: str) -> bool: point1 (str): name of the point object at the I-End of the frame object. point2 (str): name of the point object at the J-End of the frame object. """ - return self.EditFrame.ChangeConnectivity(name, point1, point2) + return self.__EditFrame.ChangeConnectivity(name, point1, point2) class Prop: def __init__(self, mySapObject) -> None: - self.SapModel=mySapObject.SapModel + self.__mySapObject=mySapObject.SapModel def __len__(self) -> int: return self.total() @@ -87,9 +87,9 @@ def __len__(self) -> int: @smooth_sap_do def rename(self, old_name: str, new_name: str): """changes the name of an existing frame section property.""" - return self.SapModel.PropFrame.ChangeName(old_name, new_name) + return self.__mySapObject.PropFrame.ChangeName(old_name, new_name) @smooth_sap_do def total(self) -> int: """returns the total number of defined frame section properties in the model""" - return self.SapModel.PropFrame.Count() \ No newline at end of file + return self.__mySapObject.PropFrame.Count() \ No newline at end of file diff --git a/src/ak_sap/Object/helper.py b/src/ak_sap/Object/helper.py index 4c18030..24c5fec 100644 --- a/src/ak_sap/Object/helper.py +++ b/src/ak_sap/Object/helper.py @@ -5,9 +5,9 @@ class MasterObj: def __init__(self, mySapObject, ElemObj) -> None: - self.mySapObject = mySapObject - self.SapModel = self.mySapObject.SapModel - self.ElemObj = ElemObj + self.__mySapObject = mySapObject + self.__SapModel = mySapObject.SapModel + self.__ElemObj = ElemObj print(f'`{self.__class__.__name__}` instance initialized.') def __str__(self) -> str: @@ -18,14 +18,14 @@ def __repr__(self) -> str: def __del__(self) -> None: try: - self.mySapObject = None - self.SapModel = None + self.__mySapObject = None + self.__SapModel = None except Exception as e: log.warning(msg=f'Exception faced when deleting {self.__class__.__name__}\n{e}') def __len__(self) -> int: """returns the total number of point elements in the analysis model.""" - return self.ElemObj.Count() + return self.__ElemObj.Count() def selected(self) -> Generator[str, Any, None]: """Returns the names of selected element objects""" @@ -36,19 +36,19 @@ def selected(self) -> Generator[str, Any, None]: def is_selected(self, name:str) -> bool: self.check_obj_legal(name) - return self.ElemObj.GetSelected(name)[0] + return self.__ElemObj.GetSelected(name)[0] @smooth_sap_do def all(self) -> tuple[str]: """Returns namelist of all element objects""" - _, *elem_list = self.ElemObj.GetNameList() + _, *elem_list = self.__ElemObj.GetNameList() return elem_list # type: ignore @smooth_sap_do def rename(self, old_name: str, new_name: str): """Change the name of the element""" self.check_obj_legal(name=old_name) - return self.ElemObj.ChangeName(old_name, new_name) + return self.__ElemObj.ChangeName(old_name, new_name) def check_obj_legal(self, name: str): """Confirms specified element exists in the model""" @@ -57,4 +57,4 @@ def check_obj_legal(self, name: str): @smooth_sap_do def delete(self, name: str): """Delete element from model""" - return self.ElemObj.Delete(name) \ No newline at end of file + return self.__ElemObj.Delete(name) \ No newline at end of file diff --git a/src/ak_sap/Object/obj.py b/src/ak_sap/Object/obj.py index 6db0121..a7e4722 100644 --- a/src/ak_sap/Object/obj.py +++ b/src/ak_sap/Object/obj.py @@ -1,7 +1,20 @@ from .point import Point from .frame import Frame +from ak_sap.utils.decorators import smooth_sap_do class Object: - def __init__(self, mySapObject) -> None: + def __init__(self, mySapObject) -> None: + self.__mySapObject = mySapObject self.Point = Point(mySapObject=mySapObject) - self.Frame = Frame(mySapObject=mySapObject) \ No newline at end of file + self.Frame = Frame(mySapObject=mySapObject) + + @smooth_sap_do + def move_selected(self, dx: float, dy: float, dz: float) -> bool: + """moves selected point, frame, cable, tendon, area, solid and link objects. + + Args: + dx (float): x offsets + dy (float): y offsets + dz (float): z offsets + """ + self.__mySapObject.SapModel.EditGeneral.Move(dx, dy, dz) \ No newline at end of file diff --git a/src/ak_sap/Object/point.py b/src/ak_sap/Object/point.py index 22659c8..7224176 100644 --- a/src/ak_sap/Object/point.py +++ b/src/ak_sap/Object/point.py @@ -7,8 +7,8 @@ class Point(MasterObj): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.PointObj) - self.EditPoint = mySapObject.SapModel.EditPoint - self.PointObj = mySapObject.SapModel.PointObj + self.__EditPoint = mySapObject.SapModel.EditPoint + self.__PointObj = mySapObject.SapModel.PointObj @smooth_sap_do def add_by_coord(self, point: tuple[float, float, float], name: str='', coord_sys: str = 'Global') -> str: @@ -33,19 +33,19 @@ def align(self, axis: Literal['X', 'Y', 'Z'], ordinate: float) -> tuple: array of the name of each point object that is in a new location after the alignment is complete.) """ _axis = ['X','Y','Z'].index(axis.upper().strip()) + 1 - return self.EditPoint.Align(_axis, ordinate) + return self.__EditPoint.Align(_axis, ordinate) @smooth_sap_do def select(self, name: str) -> bool: - return self.PointObj.SetSelected(name, True) + return self.__PointObj.SetSelected(name, True) @smooth_sap_do def deselect(self, name: str) -> bool: - return self.PointObj.SetSelected(name, False) + return self.__PointObj.SetSelected(name, False) @smooth_sap_do def deselect_all(self) -> bool: - return self.PointObj.ClearSelection() + return self.__PointObj.ClearSelection() @smooth_sap_do def merge(self, tolerance: float) -> tuple: @@ -58,7 +58,7 @@ def merge(self, tolerance: float) -> tuple: tuple: (number of the selected point objects that still exist after the merge is complete., array of the name of each selected point object that still exists after the merge is complete.) """ - return self.EditPoint.Merge(tolerance) + return self.__EditPoint.Merge(tolerance) @smooth_sap_do def change_coordinate(self, name: str, x: float, y: float, z: float) -> bool: @@ -73,4 +73,4 @@ def change_coordinate(self, name: str, x: float, y: float, z: float) -> bool: Returns: bool: Success """ - return self.EditPoint.ChangeCoordinates_1(name, x, y, z) \ No newline at end of file + return self.__EditPoint.ChangeCoordinates_1(name, x, y, z) \ No newline at end of file From a0f541ccf1742474c1665722c4fbbb2ecde56820 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:21:50 -0800 Subject: [PATCH 10/16] refactored to remove useless attributes --- src/ak_sap/Loads/LoadCases.py | 16 ++++++++-------- src/ak_sap/Loads/LoadCombos.py | 4 ++-- src/ak_sap/Loads/LoadPatterns.py | 20 ++++++++++---------- src/ak_sap/Material/material.py | 20 ++++++++++---------- src/ak_sap/Results/Setup.py | 16 ++++++++-------- src/ak_sap/Results/main.py | 4 ++-- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/ak_sap/Loads/LoadCases.py b/src/ak_sap/Loads/LoadCases.py index 3ff01ea..6f41f4e 100644 --- a/src/ak_sap/Loads/LoadCases.py +++ b/src/ak_sap/Loads/LoadCases.py @@ -7,7 +7,7 @@ class LoadCase(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) - self.LoadCases = mySapObject.SapModel.LoadCases + self.__LoadCases = mySapObject.SapModel.LoadCases def __str__(self) -> str: return 'Instance of `LoadCase`. Holds collection of functions' @@ -21,32 +21,32 @@ def total(self, casetype: LoadCaseType|None = None): """returns the total number of defined load cases in the model. If desired, counts can be returned for all load cases of a specified type in the model.""" if casetype is None: - _ret = self.LoadCases.Count() + _ret = self.__LoadCases.Count() else: _value = typing.get_args(LoadCaseType).index(casetype) + 1 - _ret = self.LoadCases.Count(_value) + _ret = self.__LoadCases.Count(_value) return _ret @smooth_sap_do def rename(self, old_name: str, new_name: str): """changes the name of an existing load case.""" assert old_name in self.list_all(), f'"{old_name}" is not in list of defined load cases {self.list_all()}' - return self.LoadCases.ChangeName(old_name, new_name) + return self.__LoadCases.ChangeName(old_name, new_name) @smooth_sap_do def list_all(self) -> tuple[str]: """retrieves the names of all defined load cases of the specified type.""" - _, loadcases, _ret = self.LoadCases.GetNameList_1() + _, loadcases, _ret = self.__LoadCases.GetNameList_1() return *loadcases, _ret @smooth_sap_do def delete(self, name: str): assert name in self.list_all(), f'"{name}" is not in list of defined load cases {self.list_all()}' - return self.LoadCases.Delete(name) + return self.__LoadCases.Delete(name) @smooth_sap_do def case_info(self, name: str) -> dict: - _ret = self.LoadCases.GetTypeOAPI_2(name) + _ret = self.__LoadCases.GetTypeOAPI_2(name) _value = { 'CaseType': typing.get_args(LoadCaseType)[_ret[0]-1], 'DesignType': typing.get_args(LoadPatternType)[_ret[2]-1], @@ -66,4 +66,4 @@ def case_info(self, name: str) -> dict: def set_type(self, name: str, casetype: LoadCaseType): assert name in self.list_all(), f'"{name}" is not in list of defined load cases {self.list_all()}' _value = typing.get_args(LoadCaseType).index(casetype) + 1 - return self.LoadCases.SetDesignType(name, 1, _value) \ No newline at end of file + return self.__LoadCases.SetDesignType(name, 1, _value) \ No newline at end of file diff --git a/src/ak_sap/Loads/LoadCombos.py b/src/ak_sap/Loads/LoadCombos.py index a3065e0..f44c602 100644 --- a/src/ak_sap/Loads/LoadCombos.py +++ b/src/ak_sap/Loads/LoadCombos.py @@ -4,7 +4,7 @@ class LoadCombo(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) - self.RespCombo = mySapObject.SapModel.RespCombo + self.__RespCombo = mySapObject.SapModel.RespCombo def __str__(self) -> str: return 'Instance of `LoadCombo`' @@ -12,4 +12,4 @@ def __str__(self) -> str: @smooth_sap_do def list_all(self) -> tuple[str]: """retrieves the names of all defined response combinations""" - return self.RespCombo.GetNameList()[1:] \ No newline at end of file + return self.__RespCombo.GetNameList()[1:] \ No newline at end of file diff --git a/src/ak_sap/Loads/LoadPatterns.py b/src/ak_sap/Loads/LoadPatterns.py index d84a5e6..55c4923 100644 --- a/src/ak_sap/Loads/LoadPatterns.py +++ b/src/ak_sap/Loads/LoadPatterns.py @@ -7,7 +7,7 @@ class LoadPattern(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) - self.LoadPatterns = mySapObject.SapModel.LoadPatterns + self.__LoadPatterns = mySapObject.SapModel.LoadPatterns def __str__(self) -> str: return 'Instance of `LoadPattern`. Holds collection of functions' @@ -17,48 +17,48 @@ def __repr__(self) -> str: def __len__(self) -> int: """returns the number of defined load patterns.""" - return self.LoadPatterns.Count() + return self.__LoadPatterns.Count() @smooth_sap_do def add(self, name: str, pattern_type: LoadPatternType, selfwt_multiplier: float=0, add_case: bool=False): """adds a new load pattern.""" chosen_pattern = typing.get_args(LoadPatternType).index(pattern_type) + 1 - return self.LoadPatterns.Add(name, chosen_pattern, + return self.__LoadPatterns.Add(name, chosen_pattern, selfwt_multiplier, add_case) @smooth_sap_do def rename(self, old_name: str, new_name: str): """applies a new name to a load pattern.""" - return self.LoadPatterns.ChangeName(old_name, new_name) + return self.__LoadPatterns.ChangeName(old_name, new_name) @smooth_sap_do def delete(self, name: str): """deletes the specified load pattern.""" - return self.LoadPatterns.Delete(name) + return self.__LoadPatterns.Delete(name) @smooth_sap_do def set_loadtype(self, name: str, pattern_type: LoadPatternType): """assigns a load type to a load pattern.""" chosen_pattern = typing.get_args(LoadPatternType).index(pattern_type) + 1 - return self.LoadPatterns.SetLoadType(name, chosen_pattern) + return self.__LoadPatterns.SetLoadType(name, chosen_pattern) @smooth_sap_do def get_loadtype(self, name: str) -> str: """assigns a load type to a load pattern.""" - value = self.LoadPatterns.GetLoadType(name) + value = self.__LoadPatterns.GetLoadType(name) chosen_pattern = typing.get_args(LoadPatternType)[value[0] - 1] return chosen_pattern, 0 # type: ignore @smooth_sap_do def set_selfwt_multiplier(self, name: str, selfwt_multiplier: float): - return self.LoadPatterns.SetSelfWtMultiplier(name, selfwt_multiplier) + return self.__LoadPatterns.SetSelfWtMultiplier(name, selfwt_multiplier) @smooth_sap_do def get_selfwt_multiplier(self, name: str) -> float: - return self.LoadPatterns.GetSelfWtMultiplier(name) + return self.__LoadPatterns.GetSelfWtMultiplier(name) @smooth_sap_do def list_all(self) -> tuple[str]: - values = self.LoadPatterns.GetNameList() + values = self.__LoadPatterns.GetNameList() return values[1:] \ No newline at end of file diff --git a/src/ak_sap/Material/material.py b/src/ak_sap/Material/material.py index 30dd41a..d6f61bf 100644 --- a/src/ak_sap/Material/material.py +++ b/src/ak_sap/Material/material.py @@ -9,7 +9,7 @@ class Material(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) - self.PropMaterial = mySapObject.SapModel.PropMaterial + self.__PropMaterial = mySapObject.SapModel.PropMaterial #Submodules self.Rebar = Rebar(mySapObject=mySapObject) @@ -20,24 +20,24 @@ def __len__(self) -> int: @smooth_sap_do def rename(self, old: str, new: str): """changes the name of an existing material property.""" - return self.PropMaterial.ChangeName(old, new) + return self.__PropMaterial.ChangeName(old, new) @smooth_sap_do def total(self) -> int: """returns the total number of defined material properties in the model.""" - return self.PropMaterial.Count() + return self.__PropMaterial.Count() @smooth_sap_do def delete(self, name: str): - return self.PropMaterial.Delete(name) + return self.__PropMaterial.Delete(name) @smooth_sap_do def list_all(self) -> list[str]: """retrieves the names of all defined material properties of the specified type""" - return self.PropMaterial.GetNameList()[1:] + return self.__PropMaterial.GetNameList()[1:] def __get_type(self, name: str) -> dict: - *_result, _ret = self.PropMaterial.GetTypeOAPI(name) + *_result, _ret = self.__PropMaterial.GetTypeOAPI(name) assert _ret == 0 return { 'MaterialType': typing.get_args(MaterialTypesStr)[_result[0] - 1], @@ -46,7 +46,7 @@ def __get_type(self, name: str) -> dict: def get_props(self, name: str) -> dict: """retrieves some basic material property data.""" - *_result, _ret = self.PropMaterial.GetMaterial(name) + *_result, _ret = self.__PropMaterial.GetMaterial(name) assert _ret == 0 return { 'MaterialType': typing.get_args(MaterialTypesStr)[_result[0] - 1], @@ -60,14 +60,14 @@ def get_props(self, name: str) -> dict: def add(self, name: str, material_type: MaterialTypesStr): """initializes a material property. If this function is called for an existing material property, all items for the material are reset to their default value.""" - return self.PropMaterial.SetMaterial(name, typing.get_args(MaterialTypesStr).index(material_type) + 1) + return self.__PropMaterial.SetMaterial(name, typing.get_args(MaterialTypesStr).index(material_type) + 1) @smooth_sap_do def set_isotropic(self, name: str, E: float, poisson: float, thermal_coeff: float): """sets the material directional symmetry type to isotropic, and assigns the isotropic mechanical properties.""" - return self.PropMaterial.SetMPIsotropic(name, E, poisson, thermal_coeff) + return self.__PropMaterial.SetMPIsotropic(name, E, poisson, thermal_coeff) @smooth_sap_do def set_density(self, name: str, mass_per_vol: float): """assigns weight per unit volume or mass per unit volume to a material property.""" - return self.PropMaterial.SetWeightAndMass(name, 2, mass_per_vol) + return self.__PropMaterial.SetWeightAndMass(name, 2, mass_per_vol) diff --git a/src/ak_sap/Results/Setup.py b/src/ak_sap/Results/Setup.py index e847ee5..ba18985 100644 --- a/src/ak_sap/Results/Setup.py +++ b/src/ak_sap/Results/Setup.py @@ -5,34 +5,34 @@ class ResultsSetup(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) - self.Setup = mySapObject.SapModel.Results.Setup + self.__Setup = mySapObject.SapModel.Results.Setup @smooth_sap_do def clear_casecombo(self): """deselects all load cases and response combinations for output.""" - return self.Setup.DeselectAllCasesAndCombosForOutput() + return self.__Setup.DeselectAllCasesAndCombosForOutput() def is_selected_case(self, casename: str): """checks if an load case is selected for output.""" - return self.Setup.GetCaseSelectedForOutput(casename) + return self.__Setup.GetCaseSelectedForOutput(casename) def select_case(self, casename: str): """sets an load case selected for output flag.""" - return self.Setup.SetCaseSelectedForOutput(casename) + return self.__Setup.SetCaseSelectedForOutput(casename) def is_selected_combo(self, comboname: str): """checks if an load combo is selected for output.""" - return self.Setup.GetComboSelectedForOutput(comboname) + return self.__Setup.GetComboSelectedForOutput(comboname) def select_combo(self, comboname: str): """sets an load combo selected for output flag.""" - return self.Setup.SetComboSelectedForOutput(comboname) + return self.__Setup.SetComboSelectedForOutput(comboname) def base_rxn_loc_get(self): """retrieves the global coordinates of the location at which the base reactions are reported.""" - *coord, _ret = self.Setup.GetOptionBaseReactLoc() + *coord, _ret = self.__Setup.GetOptionBaseReactLoc() return {'x':coord[0], 'y':coord[1], 'z':coord[2]}, _ret def set_rxn_loc_get(self, x: float, y: float, z: float): """sets the global coordinates of the location at which the base reactions are reported.""" - return self.Setup.SetOptionBaseReactLoc(x, y, z) + return self.__Setup.SetOptionBaseReactLoc(x, y, z) diff --git a/src/ak_sap/Results/main.py b/src/ak_sap/Results/main.py index 204625b..5ff257a 100644 --- a/src/ak_sap/Results/main.py +++ b/src/ak_sap/Results/main.py @@ -7,14 +7,14 @@ class Results(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) - self.Results = mySapObject.SapModel.Results + self.__Results = mySapObject.SapModel.Results self.Setup = ResultsSetup(mySapObject=mySapObject) @smooth_sap_do def joint_reactions(self, jointname:str) -> dict: loads: list[dict] = [] - _, Obj, Elm, LoadCase,StepType, StepNum, F1, F2, F3, M1, M2, M3, ret = self.Results.JointReact(jointname, 1) + _, Obj, Elm, LoadCase,StepType, StepNum, F1, F2, F3, M1, M2, M3, ret = self.__Results.JointReact(jointname, 1) for _Obj, _Elm, _LoadCase, _StepType, _StepNum, _F1, _F2, _F3, _M1, _M2, _M3 in zip(Obj, Elm, LoadCase, StepType, StepNum, F1, F2, F3, M1, M2, M3): loads.append( From 3d8e3527aa851bcc9baf76ada0e1a5273d1c44a9 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:46:59 -0800 Subject: [PATCH 11/16] General Obj Edit Functions --- documentation/Layout.md | 21 +++++++++++++++++++++ documentation/Usage.ipynb | 28 +++++++++++++++++++++++++++- src/ak_sap/Object/obj.py | 29 ++++++++++++++++++++++++++++- src/ak_sap/Object/point.py | 2 +- src/ak_sap/__init__.py | 1 + src/ak_sap/misc/__init__.py | 1 + src/ak_sap/misc/coordinates.py | 10 ++++++++++ 7 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/ak_sap/misc/__init__.py create mode 100644 src/ak_sap/misc/coordinates.py diff --git a/documentation/Layout.md b/documentation/Layout.md index 6fb4715..50e3688 100644 --- a/documentation/Layout.md +++ b/documentation/Layout.md @@ -58,6 +58,18 @@ sap.Model.set_logs('Add this comment') #Adds user comments/logs Collection of methods and attributes that apply changes to elements in the model +```python +object = sap.Object +object.move_selected(dx=0.5, dy=0, dz=1.0) #Move selected object +object.copy(dx=0.5, dy=0, dz=0, num=10)#copy selected object + +#Mirror and create object +from ak_sap import Coord +pt1 = Coord(x=10, y=20, z=0) +p2 = Coord(x=10, y=30, z=0) +object.mirror(plane='Z', coord1=pt1, coord2=pt2) #Mirror replicate selected obj. +``` + ### 2.2.1. Point Manipulate Point Elements @@ -74,6 +86,15 @@ points.all() #Lists all defined points points.rename(old_name='1', new_name='1_1') #Rename point points.check_obj_legal(name='1') #Asserts point's existance points.delete(name='1') #Delete point + +#Manipilate +points.deselect_all() #Deselect all points +points.select(name='1') #Select a single point +points.align(axis='Z', ordinate = 100) #Align selected points +points.deselect(name='1') #Deselect a single point + +points.merge(tolerance=2) #Merge points that are within tol +points.change_coord(name='1', x=0, y=0, z=0)#Change point coordinate ``` ### 2.2.2. Frames diff --git a/documentation/Usage.ipynb b/documentation/Usage.ipynb index 642b0e9..f38c7e7 100644 --- a/documentation/Usage.ipynb +++ b/documentation/Usage.ipynb @@ -93,6 +93,23 @@ "### Element" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "object = sap.Object\n", + "object.move_selected(dx=0.5, dy=0, dz=1.0) #Move selected object\n", + "object.copy(dx=0.5, dy=0, dz=0, num=10)#copy selected object\n", + "\n", + "#Mirror and create object\n", + "from ak_sap import Coord\n", + "pt1 = Coord(x=10, y=20, z=0)\n", + "p2 = Coord(x=10, y=30, z=0)\n", + "object.mirror(plane='Z', coord1=pt1, coord2=pt2) #Mirror replicate selected obj." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -114,7 +131,16 @@ "points.all() #Lists all defined points\n", "points.rename(old_name='1', new_name='1_1') #Rename point\n", "points.check_obj_legal(name='1') #Asserts point's existance\n", - "points.delete(name='1') #Delete point" + "points.delete(name='1') #Delete point\n", + "\n", + "#Manipilate\n", + "points.deselect_all() #Deselect all points\n", + "points.select(name='1') #Select a single point\n", + "points.align(axis='Z', ordinate = 100) #Align selected points\n", + "points.deselect(name='1') #Deselect a single point\n", + "\n", + "points.merge(tolerance=2) #Merge points that are within tol\n", + "points.change_coord(name='1', x=0, y=0, z=0)#Change point coordinate" ] }, { diff --git a/src/ak_sap/Object/obj.py b/src/ak_sap/Object/obj.py index a7e4722..a61b9c1 100644 --- a/src/ak_sap/Object/obj.py +++ b/src/ak_sap/Object/obj.py @@ -1,10 +1,14 @@ +from typing import Literal + from .point import Point from .frame import Frame from ak_sap.utils.decorators import smooth_sap_do +from ak_sap.misc import Coord class Object: def __init__(self, mySapObject) -> None: self.__mySapObject = mySapObject + self.__EditGeneral = mySapObject.SapModel.EditGeneral self.Point = Point(mySapObject=mySapObject) self.Frame = Frame(mySapObject=mySapObject) @@ -17,4 +21,27 @@ def move_selected(self, dx: float, dy: float, dz: float) -> bool: dy (float): y offsets dz (float): z offsets """ - self.__mySapObject.SapModel.EditGeneral.Move(dx, dy, dz) \ No newline at end of file + self.__EditGeneral.Move(dx, dy, dz) + + @smooth_sap_do + def copy(self, dx: float, dy: float, dz: float, num: int) -> tuple: + """linearly replicates selected objects. + + Args: + dx (float): x offset + dy (float): y offset + dz (float): z offset + num (int): number of times the selected objects are to be replicated. + """ + return self.__EditGeneral.ReplicateLinear(dx, dy, dz, num) + + @smooth_sap_do + def mirror(self, plane: Literal['X', 'Y', 'Z'], coord1: Coord, coord2: Coord): + """mirror replicates selected objects + + Args: + plane (Literal['X', 'Y', 'Z']): parallel to this plane + coord1 (Coord), coord2 (Coord): define the intersection of the mirror plane with the perp. plane + """ + axis = ['Z', 'X', 'Y'].index(plane.upper().strip()) + 1 + return self.__EditGeneral.ReplicateMirror(axis, *coord1.as_tuple(), *coord2.as_tuple()) \ No newline at end of file diff --git a/src/ak_sap/Object/point.py b/src/ak_sap/Object/point.py index 7224176..1ed1ca6 100644 --- a/src/ak_sap/Object/point.py +++ b/src/ak_sap/Object/point.py @@ -61,7 +61,7 @@ def merge(self, tolerance: float) -> tuple: return self.__EditPoint.Merge(tolerance) @smooth_sap_do - def change_coordinate(self, name: str, x: float, y: float, z: float) -> bool: + def change_coord(self, name: str, x: float, y: float, z: float) -> bool: """changes the coordinates of a specified point object. Args: diff --git a/src/ak_sap/__init__.py b/src/ak_sap/__init__.py index 29d5206..83f19bd 100644 --- a/src/ak_sap/__init__.py +++ b/src/ak_sap/__init__.py @@ -3,6 +3,7 @@ from ak_sap.utils.logger import log from ak_sap.wrapper import Sap2000Wrapper +from ak_sap.misc import Coord #log = Log() diff --git a/src/ak_sap/misc/__init__.py b/src/ak_sap/misc/__init__.py new file mode 100644 index 0000000..063333f --- /dev/null +++ b/src/ak_sap/misc/__init__.py @@ -0,0 +1 @@ +from .coordinates import Coord \ No newline at end of file diff --git a/src/ak_sap/misc/coordinates.py b/src/ak_sap/misc/coordinates.py new file mode 100644 index 0000000..62c275b --- /dev/null +++ b/src/ak_sap/misc/coordinates.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass + +@dataclass +class Coord: + x: float + y: float + z: float + + def as_tuple(self) -> tuple[float]: + return (self.x, self.y, self.z) \ No newline at end of file From 11a43995c9eb1714256fd37487dda97e42019b61 Mon Sep 17 00:00:00 2001 From: Arun Kishore <50844460+rpakishore@users.noreply.github.com> Date: Fri, 8 Mar 2024 10:58:59 -0800 Subject: [PATCH 12/16] `sap.Analyze` Options Added --- documentation/Layout.md | 136 ++++++++++++++++++++++----------- documentation/Usage.ipynb | 67 +++++++++++++--- src/ak_sap/Analyze/__init__.py | 1 + src/ak_sap/Analyze/main.py | 123 +++++++++++++++++++++++++++++ src/ak_sap/Results/main.py | 10 +++ src/ak_sap/wrapper.py | 2 + src/tests/Analyze/test_main.py | 61 +++++++++++++++ 7 files changed, 345 insertions(+), 55 deletions(-) create mode 100644 src/ak_sap/Analyze/__init__.py create mode 100644 src/ak_sap/Analyze/main.py create mode 100644 src/tests/Analyze/test_main.py diff --git a/documentation/Layout.md b/documentation/Layout.md index 50e3688..4ec74c7 100644 --- a/documentation/Layout.md +++ b/documentation/Layout.md @@ -1,38 +1,66 @@