diff --git a/README.md b/README.md index a6d5b6d..e426ee4 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,30 @@ - [2.1. Prerequisites](#21-prerequisites) - [2.2. Installation](#22-installation) - [2.2.1. Production](#221-production) - - [2.2.1.1. One line command](#2211-one-line-command) - - [2.2.1.2. Install directly from repo](#2212-install-directly-from-repo) - - [2.2.1.3. Install from Pypi release](#2213-install-from-pypi-release) + - [2.2.1.1. Install directly from repo](#2211-install-directly-from-repo) + - [2.2.1.2. Install from Pypi release](#2212-install-from-pypi-release) - [2.2.2. Development](#222-development) - [3. Usage](#3-usage) - [3.1. GUI](#31-gui) - [3.2. Layout Documentation](#32-layout-documentation) + - [Layout Map](#layout-map) + - [Initialize](#initialize) + - [Parent Level](#parent-level) + - [Sub-Modules](#sub-modules) + - [Model](#model) + - [Element](#element) + - [Point](#point) + - [Frame](#frame) + - [Database](#database) + - [Select](#select) + - [Loads](#loads) + - [Load Patterns](#load-patterns) + - [Load Cases](#load-cases) + - [Modal](#modal) + - [Analyze](#analyze) + - [Results](#results) + - [Material](#material) + - [Rebar](#rebar) - [4. Roadmap](#4-roadmap) - [5. License](#5-license) - [6. Contact](#6-contact) @@ -59,21 +76,7 @@ #### 2.2.1. Production -##### 2.2.1.1. One line command - -1. Press `Win` + `R` to open the Run console -2. Type "cmd" and press enter -3. Type the following and press `Enter` - - ```cmd - curl -sSL https://links.struct.work/SAP2000 > %USERPROFILE%\Desktop\install.bat - - ``` - -4. You should now have a `install.bat` file in your desktop -5. Move this file to your desired installtion directory and run to install the `AK_SAP` module - -##### 2.2.1.2. Install directly from repo +##### 2.2.1.1. Install directly from repo Clone repo and Install with flit @@ -95,7 +98,7 @@ pip install flit flit install --deps production --extras gui ``` -##### 2.2.1.3. Install from Pypi release +##### 2.2.1.2. Install from Pypi release ```bash pip install ak_sap @@ -114,6 +117,15 @@ pip install flit flit install --pth-file ``` +Updating Docs: + +- Update the [Usage.ipynb](./documentation/Usage.ipynb). +- Open `cmd.exe` to run + + ```bash + update-doc + ``` + ## 3. Usage @@ -149,21 +161,389 @@ The repo has an optional streamlit GUI for the wrapper. Checkout [`GUI.md`](/doc ### 3.2. Layout Documentation -To see module level usage, check out the [`Layout.md`](/documentation/Layout.md) or [`Usage.ipynb`](/documentation/Usage.ipynb) + +#### Layout Map + +![MindMap](assets/mindmap.svg) + +#### Initialize + +Usage Examples: + +```python +from ak_sap import debug, Sap2000Wrapper +debug(status=False) + +#Initialize +sap = Sap2000Wrapper(attach_to_exist=True) #Attach to existing opened model +sap = Sap2000Wrapper(attach_to_exist=False) #Create new blank model from latest SAP2000 +## Create blank model from a custom version of SAP2000 +sap = Sap2000Wrapper(attach_to_exist=False, program_path=r'Path\to\SAP2000.exe') +``` + +#### Parent Level + +Usage Examples: + +```python +sap.hide() #Hide the SAP2000 window +sap.unhide() #Unhides SAP2000 window +sap.ishidden #Check if window is hidden +sap.version #Returns SAP2000 version number +sap.api_version #Returns Sap0API version number +sap.exit(save=False) #Exit the application + +sap.save(r'\Path\to\save\file.sdb') +``` + +#### Sub-Modules +##### Model + +Collection of methods and attributes that control changes to the model as a whole + +Usage Examples: + +```python +sap.Model.units #Returns current model units +sap.Model.units_database #Returns Internal Database units +sap.Model.set_units(value='N_m_C') #Changes the present units of model + +sap.Model.merge_tol #retrieves the value of the program auto merge tolerance +sap.Model.set_merge_tol(0.05) #sets the program auto merge tolerance + +sap.Model.filepath #Returns filepath of current file + +sap.Model.is_locked #Returns if the model is locked +sap.Model.lock() #Locks the model +sap.Model.unlock() #Unlocks the model + +sap.Model.project_info #Returns a dict of Project Info +##Set project info, use `.project_info` to see available keys +sap.Model.set_project_info({'Design Code': 'BCBC 2018'}) + +sap.Model.logs #Retrieve user comments and logs +sap.Model.set_logs('Add this comment') #Adds user comments/logs +``` + +##### Element + +Collection of methods and attributes that apply changes to elements in the model + +Usage Examples: + +```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. +``` + +###### Point + +Manipulate Point Elements + +Usage Examples: + +```python +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_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 + +# Extrude point to frame +points.extrude( + point_name='3', + property_name='FSec1', + dx=0, dy=144, dz=0, + num_frames=3 +) +points.merge(tolerance=2) #Merge points that are within tol +points.change_coord(name='1', x=0, y=0, z=0)#Change point coordinate +``` + +###### Frame + +Manipulate Frame Elements + +Usage Examples: + +```python +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_obj_legal(name='1') #Asserts frame's existance +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 + +# Extrude frames to area +frames.extrude( + frame_name='8', + property_name='Default', + dx=0, dy=144, dz=0, + num_areas=3, + del_frame=True +) + +# Get frame properties +frames.Prop.rename(old_name="FSEC1", new_name="MySection") #Rename frame property +frames.Prop.total() #Total # of defined frame properties +``` + +##### Database + +Control the database values + +Usage Examples: + +```python +tables = sap.Table +tables.list_available() #Lists available database tables +tables.list_all() #Lists all database tables +tables.get_table_fields('Analysis Options') #Get table Field Info +tables.get(TableKey='Load Case Definitions', dataframe=False) #Get Table data in `list[dict]` format +df = tables.get('Material Properties 01 - General') #Get Table data in pandas dataframe + +# Update Table +df.iloc[0,0] = 'New Value' +tables.update(TableKey='Material Properties 01 - General', data=df, apply=True) +``` + +##### Select + +Usage Examples: + +```python +select = sap.Select + +select.all() #Select all objects +select.clear() #Deselect all objects + +select.constraint(name='Diaph1') #Select points in constraint +select.constraint(name='Diaph1', reverse=True) #Deselect points in constraint + +select.invert() #Invert selections +select.selected #Returns list of selected objects +select.previous() #restores the previous selection + +#Selection based on plane +select.in_plane(pointname='1', plane='XY') #Select in XY plane +select.in_plane(pointname='2', plane='YZ', reverse=False) #Deselect + +#Select by property +select.property(type='Area', name='ASEC1') +select.property(type='Cable', name='CAB1', reverse=True) +select.property(type='Frame', name='FSEC1') +select.property(type='Link', name='GAP1', reverse=True) +select.property(type='Material', name='A992Fy50') +select.property(type='Solid', name='SOLID1', reverse=True) +select.property(type='Tendon', name='TEN1') +``` + +##### Loads + +Control the definition and assignments of loads. +###### Load Patterns + +Usage Examples: + +```python +pattern = sap.Load.Pattern +len(pattern) # List the number of load patterns defined +pattern.list_all() #List defined load patterns +pattern.rename('Dead', 'Live') #Rename previously defined pattern +pattern.delete(name='Dead') #Delete a load pattern + +pattern.get_selfwt_multiplier('DEAD') #Get defined self weight multiplier +pattern.set_selfwt_multiplier('DEAD', 1.15) #Set self weight multiplier + +pattern.get_loadtype('DEAD') #Get the defined load type +pattern.set_loadtype('DEAD', pattern_type='LIVE') #Set the defined load type + +#Add a Live load case with name "Custom Live", a 1.15x self weight multiplier and also generate an accompanying load case +pattern.add(name='Custom Live', pattern_type='LIVE', + selfwt_multiplier=1.15, add_case=True) +``` + +###### Load Cases + +Usage Examples: + +```python +cases = sap.Load.Case +len(cases) #returns total # of defined cases +cases.total(casetype='MODAL') #Get # of modal load cases +cases.list_all() #List all load cases +cases.rename('DEAD','WATER') #Rename existing load case +cases.case_info(name='DEAD') #Get the Case type information +cases.set_type(name='DEAD', casetype='LINEAR_STATIC') #Change the case type of existing load case +``` + +###### Modal + +`sap.Load.Modal` +####### Eigen + +Usage Examples: + +```python +eigen = sap.Load.Modal.Eigen +eigen.set_case(case_name="LCASE1") #Set a Eigen Modal case + +eigen.set_initial_case(case_name='LCASE1', initial_case='DEAD') #Set initial stiffness case +eigen.get_initial_case(case_name="LCASE1") #Get the Initial Case + +eigen.get_loads(case_name='LCASE1') #Get the load data + +#Set Eigen parameters +eigen.set_parameters( + case_name='LCASE1', + EigenShiftFreq=0.05, #cyc/s + EigenCutOff=0.0001, #cyc/s + EigenTolerance=0.00000001, + AllowAutoFreqShift=True +) +eigen.get_parameters(case_name='LCASE1') #Get Parameters + +eigen.set_number_modes(case_name='LCASE1', max=10, min=5) #set number of modes +eigen.get_number_modes(case_name='LCASE1') #get number of modes +``` + +####### Ritz + +Usage Examples: + +```python +ritz = sap.Load.Modal.Ritz +ritz.set_case(case_name="LCASE1") #Set a Eigen Modal case + +ritz.set_initial_case(case_name='LCASE1', initial_case='DEAD') #Set initial stiffness case +ritz.get_initial_case(case_name="LCASE1") #Get the Initial Case + +ritz.get_loads(case_name='LCASE1') #Get the load data + +ritz.set_number_modes(case_name='LCASE1', max=10, min=5) #set number of modes +ritz.get_number_modes(case_name='LCASE1') #get number of modes +``` + +##### Analyze + +Usage Examples: + +```python +analyze = sap.Analyze +analyze.create_model() #Create analysis model +analyze.run() #Runs the analysis +analyze.case_status() #retrieves the status for all load cases. +analyze.get_run_flag() #retrieves the run flags for all cases +analyze.set_run_flag(case='MODAL', status=True) # Set case to run +analyze.get_solver() #Get solver info + +#Set solver options +analyze.set_solver( + SolverType='Standard', + SolverProcessType='Auto', + NumberParallelRuns=0, + StiffCase='' +) +``` + +##### Results + +Manipulate Results from SAP2000 + +Usage Examples: + +```python +results = sap.Results + +setup = sap.Results.Setup +setup.clear_casecombo() #Deselect all Case&Combo for results +setup.select_case(casename='DEAD') #sets an load case selected for output flag. +setup.is_selected_case(casename='DEAD') #checks if an load case is selected for output. +setup.select_combo(comboname='DEAD') #sets an load combo selected for output flag. +setup.is_selected_combo(comboname='COMB1') #checks if an load combo is selected for output. +setup.set_rxn_loc_get(x=0.5, y=0.5, z=5) #sets coordinates of the locn at which the base reactions are reported. +setup.base_rxn_loc_get() #retrieves coordinates of the locn at which the base reactions are reported. + +results.joint_reactions(jointname='1') #Get Joint reactions as list of dict +results.joint_displacements(jointname='1') #Get Joint displacements as list of dict +results.joint_accelerations(jointname='1') #Get joint accelerations +results.joint_velocities(jointname='1') #Get joint velocities + +results.delete('MODAL') #Delete results of `MODAL` case +results.delete('All') #Delete results of all cases +``` + +##### Material + +Usage Examples: + +```python +material = sap.Material +material.rename(old="4000Psi", new="MatConc") #Rename existing material +material.total() #Total # of defined material properties +material.delete(name='4000Psi') #Delete existing material property +material.list_all() #List all defined Material Properties +material.get_props(name='4000Psi') #Returns basic material property data +material.add(name='Steel', material_type='Steel') #Initialze Material Property +material.set_isotropic(name='Steel', E=29500, poisson=0.25, thermal_coeff=6e-06) #Set isotropic material properties +material.set_density(name='Steel', mass_per_vol=0.00029) #set density +``` + +###### Rebar + +Usage Examples: + +```python +rebar = sap.Material.Rebar +rebar.rename(old='R1', new='MyRebar') #Rename rebar +rebar.total() #Total # of defined rebar properties +rebar.delete(name='R1') #Delete existing rebar property +rebar.list_all() #List all defined rebar Properties +rebar.set_prop(name='MyRebar2', area=1.05, dia=1.0) #Define a rebar property +rebar.get_prop(name='MyRebar2') #Get rebar property +``` + + ## 4. Roadmap -![Roadmap/Checklist](/documentation/assets/mindmap.png) - -- [ ] Generate Load Patterns -- [ ] Generate Load Cases +- [x] Generate Load Patterns +- [x] Generate Load Cases - [ ] Apply Loads - [ ] Points - [ ] Area - [ ] Line -- [ ] Export joint reactions to Hilti-Profis file -- [ ] Export Frame/Wall sections to S-Concrete +- [x] Export joint reactions to Hilti-Profis file ## 5. License diff --git a/documentation/Layout.md b/documentation/Layout.md index 07f1d27..88e4339 100644 --- a/documentation/Layout.md +++ b/documentation/Layout.md @@ -9,6 +9,7 @@ - [Point](#point) - [Frame](#frame) - [Database](#database) + - [Select](#select) - [Loads](#loads) - [Load Patterns](#load-patterns) - [Load Cases](#load-cases) @@ -49,6 +50,7 @@ sap.unhide() #Unhides SAP2000 window sap.ishidden #Check if window is hidden sap.version #Returns SAP2000 version number sap.api_version #Returns Sap0API version number +sap.exit(save=False) #Exit the application sap.save(r'\Path\to\save\file.sdb') ``` @@ -123,6 +125,13 @@ 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 +# Extrude point to frame +points.extrude( + point_name='3', + property_name='FSec1', + dx=0, dy=144, dz=0, + num_frames=3 +) points.merge(tolerance=2) #Merge points that are within tol points.change_coord(name='1', x=0, y=0, z=0)#Change point coordinate ``` @@ -153,6 +162,15 @@ 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 +# Extrude frames to area +frames.extrude( + frame_name='8', + property_name='Default', + dx=0, dy=144, dz=0, + num_areas=3, + del_frame=True +) + # Get frame properties frames.Prop.rename(old_name="FSEC1", new_name="MySection") #Rename frame property frames.Prop.total() #Total # of defined frame properties @@ -178,12 +196,16 @@ tables.update(TableKey='Material Properties 01 - General', data=df, apply=True) ``` ## Select -elect = sap.Select -select.all() #Select all objects -select.clear() #Deselect all objects +Usage Examples: -select.constraint(name='Diaph1')#Select points in constraint +```python +select = sap.Select + +select.all() #Select all objects +select.clear() #Deselect all objects + +select.constraint(name='Diaph1') #Select points in constraint select.constraint(name='Diaph1', reverse=True) #Deselect points in constraint select.invert() #Invert selections @@ -191,7 +213,7 @@ select.selected #Returns list of selected objects select.previous() #restores the previous selection #Selection based on plane -select.in_plane(pointname='1', plane='XY') #Select in XY plane +select.in_plane(pointname='1', plane='XY') #Select in XY plane select.in_plane(pointname='2', plane='YZ', reverse=False) #Deselect #Select by property @@ -202,6 +224,8 @@ select.property(type='Link', name='GAP1', reverse=True) select.property(type='Material', name='A992Fy50') select.property(type='Solid', name='SOLID1', reverse=True) select.property(type='Tendon', name='TEN1') +``` + ## Loads Control the definition and assignments of loads. @@ -297,7 +321,7 @@ analyze = sap.Analyze analyze.create_model() #Create analysis model analyze.run() #Runs the analysis analyze.case_status() #retrieves the status for all load cases. -analyze.get_run_status() #retrieves the run flags for all cases +analyze.get_run_flag() #retrieves the run flags for all cases analyze.set_run_flag(case='MODAL', status=True) # Set case to run analyze.get_solver() #Get solver info diff --git a/documentation/Usage.ipynb b/documentation/Usage.ipynb index 06af808..c489dd4 100644 --- a/documentation/Usage.ipynb +++ b/documentation/Usage.ipynb @@ -41,6 +41,7 @@ "sap.ishidden #Check if window is hidden\n", "sap.version #Returns SAP2000 version number\n", "sap.api_version #Returns Sap0API version number\n", + "sap.exit(save=False) #Exit the application\n", "\n", "sap.save(r'\\Path\\to\\save\\file.sdb')" ] @@ -145,6 +146,13 @@ "points.align(axis='Z', ordinate = 100) #Align selected points\n", "points.deselect(name='1') #Deselect a single point\n", "\n", + "# Extrude point to frame\n", + "points.extrude(\n", + " point_name='3',\n", + " property_name='FSec1',\n", + " dx=0, dy=144, dz=0,\n", + " num_frames=3\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" ] @@ -183,6 +191,15 @@ "frames.join('2','3') #Join Colinear frames\n", "frames.change_points(name='1', point1='1', point2='3') #Change connected points of frame\n", "\n", + "# Extrude frames to area\n", + "frames.extrude(\n", + " frame_name='8',\n", + " property_name='Default',\n", + " dx=0, dy=144, dz=0,\n", + " num_areas=3,\n", + " del_frame=True\n", + ")\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" @@ -223,15 +240,21 @@ ] }, { - "cell_type": "markdown", - "metadata": {}, + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], "source": [ "select = sap.Select\n", "\n", - "select.all() #Select all objects\n", - "select.clear() #Deselect all objects\n", + "select.all() #Select all objects\n", + "select.clear() #Deselect all objects\n", "\n", - "select.constraint(name='Diaph1')#Select points in constraint\n", + "select.constraint(name='Diaph1') #Select points in constraint\n", "select.constraint(name='Diaph1', reverse=True) #Deselect points in constraint\n", "\n", "select.invert() #Invert selections\n", @@ -239,7 +262,7 @@ "select.previous() #restores the previous selection\n", "\n", "#Selection based on plane\n", - "select.in_plane(pointname='1', plane='XY') #Select in XY plane\n", + "select.in_plane(pointname='1', plane='XY') #Select in XY plane\n", "select.in_plane(pointname='2', plane='YZ', reverse=False) #Deselect\n", "\n", "#Select by property\n", @@ -399,7 +422,7 @@ "analyze.create_model() #Create analysis model\n", "analyze.run() #Runs the analysis\n", "analyze.case_status() #retrieves the status for all load cases.\n", - "analyze.get_run_status() #retrieves the run flags for all cases\n", + "analyze.get_run_flag() #retrieves the run flags for all cases\n", "analyze.set_run_flag(case='MODAL', status=True) # Set case to run\n", "analyze.get_solver() #Get solver info\n", "\n", diff --git a/documentation/Usage/GUI.md b/documentation/Usage/GUI.md index f851480..0065a1a 100644 --- a/documentation/Usage/GUI.md +++ b/documentation/Usage/GUI.md @@ -20,20 +20,19 @@ The package not comes pre-compiled with the streamlit package. - Install dependencies ```bash - pip install flit && flit install + pip install flit && flit install --extras gui ``` - Launch the app run ```bash - python -m streamlit run Start_Here.py + gui ``` - Alternatively, In windows launch by executing the script ```cmd - cd scripts - run.bat + python -m streamlit run Start_Here.py ``` - Open up the SAP2000 model of your choice and click `Attach to Model` diff --git a/documentation/assets/mindmap.svg b/documentation/assets/mindmap.svg index 5ce8e9c..bf61cb9 100644 --- a/documentation/assets/mindmap.svg +++ b/documentation/assets/mindmap.svg @@ -1 +1,2 @@ -
Ritz
Eigen
Rebar
Modal
Load Cases
Load Patterns
Frames
Point
Material
Results
Loads
Table
Element
Model
AK_SAP
\ No newline at end of file + + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 31ed417..b864c5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,9 +13,7 @@ dynamic = ["version", "description"] dependencies = [ "comtypes==1.2.0", "forallpeople==2.6.7", - "pandas==2.1.3", - "rich==13.7.0", - "typer[all]==0.9.0" + "pandas==2.1.3" ] [project.optional-dependencies] @@ -32,6 +30,11 @@ gui = [ "hilti_profis==0.0.3" ] +cli = [ + "rich==13.7.0", + "typer[all]==0.9.0" +] + test = [ "pytest==7.4.3" ] @@ -40,4 +43,5 @@ test = [ Home = "https://github.com/rpakishore/ak_sap" [project.scripts] -app="ak_sap.gui.cli_app:app" \ No newline at end of file +gui="ak_sap.gui.gui_app:app" +update-doc="ak_sap.cli.update_doc:app" \ No newline at end of file diff --git a/src/ak_sap/Analyze/main.py b/src/ak_sap/Analyze/main.py index f63179e..0e0a30b 100644 --- a/src/ak_sap/Analyze/main.py +++ b/src/ak_sap/Analyze/main.py @@ -29,7 +29,7 @@ def case_status(self) -> dict: Returns: list[dict]: Cases and their current status. """ - return case_status(ret=self.__Analyze.GetCaseStatus()), 0 + return case_status(ret=self.__Analyze.GetCaseStatus()), 0 # type: ignore @smooth_sap_do def get_run_flag(self) -> dict: @@ -38,7 +38,7 @@ def get_run_flag(self) -> dict: Returns: dict: Loadcases and their run flags """ - return get_run_flag(ret=self.__Analyze.GetRunCaseFlag()), 0 + return get_run_flag(ret=self.__Analyze.GetRunCaseFlag()), 0 # type: ignore @smooth_sap_do def set_run_flag(self, case: str, status: bool): @@ -57,7 +57,7 @@ def get_solver(self) -> dict: Returns: dict: Solver Info """ - return get_solver(ret = self.__Analyze.GetSolverOption_3()), 0 + return get_solver(ret = self.__Analyze.GetSolverOption_3()), 0 # type: ignore @smooth_sap_do def set_solver(self, diff --git a/src/ak_sap/Loads/Modal/Eigen/main.py b/src/ak_sap/Loads/Modal/Eigen/main.py index 75a615a..084ed81 100644 --- a/src/ak_sap/Loads/Modal/Eigen/main.py +++ b/src/ak_sap/Loads/Modal/Eigen/main.py @@ -4,6 +4,7 @@ class Eigen(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) + assert self.SapModel is not None self.ModalEigen = self.SapModel.LoadCases.ModalEigen def __str__(self) -> str: diff --git a/src/ak_sap/Loads/Modal/Ritz/main.py b/src/ak_sap/Loads/Modal/Ritz/main.py index 06ed4ad..473eef3 100644 --- a/src/ak_sap/Loads/Modal/Ritz/main.py +++ b/src/ak_sap/Loads/Modal/Ritz/main.py @@ -4,6 +4,7 @@ class Ritz(MasterClass): def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject) + assert self.SapModel is not None self.ModalRitz = self.SapModel.LoadCases.ModalRitz def __str__(self) -> str: diff --git a/src/ak_sap/Object/frame.py b/src/ak_sap/Object/frame.py index f9cdee3..4421913 100644 --- a/src/ak_sap/Object/frame.py +++ b/src/ak_sap/Object/frame.py @@ -7,19 +7,22 @@ def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.FrameObj) self.__EditFrame = mySapObject.SapModel.EditFrame + self.__EditGeneral = mySapObject.SapModel.EditGeneral + self.__FrameObj = mySapObject.SapModel.FrameObj + self.Prop = Prop(mySapObject=mySapObject) @smooth_sap_do def get_section(self, name: str) -> str: self.check_obj_legal(name=name) - _ret = self.ElemObj.GetSection(name) + _ret = self.__FrameObj.GetSection(name) return (_ret[0], _ret[-1]) # type: ignore @smooth_sap_do 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=name) - return self.ElemObj.GetPoints(name) + return self.__FrameObj.GetPoints(name) @smooth_sap_do def divide_by_distance(self, name: str, dist: float, Iend: bool=True) -> tuple[str]: @@ -77,6 +80,24 @@ def change_points(self, name: str, point1: str, point2: str) -> bool: """ return self.__EditFrame.ChangeConnectivity(name, point1, point2) + @smooth_sap_do + def extrude(self, frame_name: str, dx: float, dy: float, dz: float, num_areas: int, property_name: str|None = None, del_frame: bool=False) -> list[str]: + """Creates new area objects by linearly extruding a specified frame obj. + + Args: + frame_name (str): Name of existing frame to extrude + dx (float): x offset. + dy (float): y offset. + dz (float): z offset. + num_areas (int): number of area objects to create + property_name (str | None, optional): Name of a defined area section property to be used for the new obj. Defaults to None. + del_frame(bool, optional): If this item is True, the straight frame object indicated by the Name item is deleted after the extrusion is complete. Defaults to False. + + Returns: + list[str]: array of the name of each area object created + """ + return self.__EditGeneral.ExtrudeFrameToAreaLinear(frame_name, property_name, dx, dy, dz, num_areas, del_frame) + class Prop: def __init__(self, mySapObject) -> None: self.__mySapObject=mySapObject.SapModel diff --git a/src/ak_sap/Object/obj.py b/src/ak_sap/Object/obj.py index a61b9c1..7208bc8 100644 --- a/src/ak_sap/Object/obj.py +++ b/src/ak_sap/Object/obj.py @@ -21,7 +21,7 @@ def move_selected(self, dx: float, dy: float, dz: float) -> bool: dy (float): y offsets dz (float): z offsets """ - self.__EditGeneral.Move(dx, dy, dz) + return self.__EditGeneral.Move(dx, dy, dz) @smooth_sap_do def copy(self, dx: float, dy: float, dz: float, num: int) -> tuple: diff --git a/src/ak_sap/Object/point.py b/src/ak_sap/Object/point.py index 1ed1ca6..0bcaac7 100644 --- a/src/ak_sap/Object/point.py +++ b/src/ak_sap/Object/point.py @@ -9,6 +9,7 @@ def __init__(self, mySapObject) -> None: super().__init__(mySapObject=mySapObject, ElemObj=mySapObject.SapModel.PointObj) self.__EditPoint = mySapObject.SapModel.EditPoint self.__PointObj = mySapObject.SapModel.PointObj + self.__EditGeneral = mySapObject.SapModel.EditGeneral @smooth_sap_do def add_by_coord(self, point: tuple[float, float, float], name: str='', coord_sys: str = 'Global') -> str: @@ -18,7 +19,7 @@ def add_by_coord(self, point: tuple[float, float, float], name: str='', coord_sy name (str, optional): Custom name for point. Defaults to ''. coord_sys (str, optional): Name of coordinate system. Defaults to 'Global'. """ - return self.ElemObj.AddCartesian(*point, '', name, coord_sys) + return self.__PointObj.AddCartesian(*point, '', name, coord_sys) @smooth_sap_do def align(self, axis: Literal['X', 'Y', 'Z'], ordinate: float) -> tuple: @@ -73,4 +74,21 @@ def change_coord(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) + + @smooth_sap_do + def extrude(self, point_name: str, dx: float, dy: float, dz: float, num_frames: int, property_name: str|None = None) -> list[str]: + """Creates new frame objects by linearly extruding a specified point obj. into frame objects. + + Args: + point_name (str): Name of existing point to extrude + dx (float): x offset. + dy (float): y offset. + dz (float): z offset. + num_frames (int): number of frame objects to create + property_name (str | None, optional): Name of a defined frame section property to be used for the new frame obj. Defaults to None. + + Returns: + list[str]: array of the name of each frame object created + """ + return self.__EditGeneral.ExtrudePointToFrameLinear(point_name, property_name, dx, dy, dz, num_frames) \ No newline at end of file diff --git a/src/ak_sap/Results/main.py b/src/ak_sap/Results/main.py index 52cc1d6..17ca04a 100644 --- a/src/ak_sap/Results/main.py +++ b/src/ak_sap/Results/main.py @@ -18,7 +18,7 @@ def delete(self, casename: str|Literal['All']) -> bool: Args: casename (str | Literal['All']): name of an existing load case that is to have its results deleted. """ - return self.mySapObject.SapModel.Analyze.DeleteResykts(casename, casename.casefold() == 'all') + return self.mySapObject.SapModel.Analyze.DeleteResykts(casename, casename.casefold() == 'all') # type: ignore @smooth_sap_do def joint_reactions(self, jointname:str) -> list[dict]: @@ -27,7 +27,7 @@ def joint_reactions(self, jointname:str) -> list[dict]: Args: jointname (str): name of an existing point object """ - return joint_reactions_parse(ret=self.__Results.JointReact(jointname, 1)), 0 + return joint_reactions_parse(ret=self.__Results.JointReact(jointname, 1)), 0 # type: ignore @smooth_sap_do @@ -37,7 +37,7 @@ def joint_displacements(self, jointname:str) -> list[dict]: Args: jointname (str): name of an existing point object """ - return joint_displacements_parse(ret=self.__Results.JointDispl(jointname, 1)), 0 + return joint_displacements_parse(ret=self.__Results.JointDispl(jointname, 1)), 0 # type: ignore @smooth_sap_do def joint_accelerations(self, jointname:str) -> list[dict]: @@ -46,7 +46,7 @@ def joint_accelerations(self, jointname:str) -> list[dict]: Args: jointname (str): name of an existing point object """ - return joint_displacements_parse(ret=self.__Results.JointAcc(jointname, 1)), 0 + return joint_displacements_parse(ret=self.__Results.JointAcc(jointname, 1)), 0 # type: ignore @smooth_sap_do def joint_velocities(self, jointname:str, format: Literal['Pandas', 'List']= 'List') -> list[dict]: @@ -55,7 +55,7 @@ def joint_velocities(self, jointname:str, format: Literal['Pandas', 'List']= 'Li Args: jointname (str): name of an existing point object """ - return joint_displacements_parse(ret=self.__Results.JointVel(jointname, 1)), 0 + return joint_displacements_parse(ret=self.__Results.JointVel(jointname, 1)), 0 # type: ignore def joint_displacements_parse(ret: list) -> list[dict]: assert ret[-1] == 0 diff --git a/src/ak_sap/Select/main.py b/src/ak_sap/Select/main.py index b44d586..2607af1 100644 --- a/src/ak_sap/Select/main.py +++ b/src/ak_sap/Select/main.py @@ -31,7 +31,7 @@ def constraint(self, name: str, reverse: bool=False) -> bool: @property def selected(self) -> list[dict]: - return selected_parse(ret=self.__SelectObj.GetSelected()), 0 + return selected_parse(ret=self.__SelectObj.GetSelected()), 0 # type: ignore @smooth_sap_do def in_plane(self, pointname: str, plane: Literal['XY', 'YZ', 'XZ'], reverse: bool=False) -> bool: diff --git a/src/ak_sap/__init__.py b/src/ak_sap/__init__.py index a68bdda..482e27b 100644 --- a/src/ak_sap/__init__.py +++ b/src/ak_sap/__init__.py @@ -1,12 +1,10 @@ "Python wrapper for SAP2000 API" -__version__ = "0.0.2" +__version__ = "0.0.3" from ak_sap.utils.logger import log from ak_sap.wrapper import Sap2000Wrapper from ak_sap.misc import Coord -#log = Log() - def debug(status=False): """Import this in a new module and enable debug to use debug example: diff --git a/src/ak_sap/gui/cli_app.py b/src/ak_sap/cli/cli_app.py similarity index 89% rename from src/ak_sap/gui/cli_app.py rename to src/ak_sap/cli/cli_app.py index 39edc0c..fa6e771 100644 --- a/src/ak_sap/gui/cli_app.py +++ b/src/ak_sap/cli/cli_app.py @@ -13,5 +13,4 @@ def template_fn(template_str: str, template_bool: bool = False): """This is a sample function to be executed through the cli app """ log.info('Called the `template_fn` function') - ic(template_bool) - ic(template_str) \ No newline at end of file + print('CLI app will be launched') \ No newline at end of file diff --git a/src/ak_sap/cli/update_doc.py b/src/ak_sap/cli/update_doc.py new file mode 100644 index 0000000..22ec8c2 --- /dev/null +++ b/src/ak_sap/cli/update_doc.py @@ -0,0 +1,47 @@ +import json +import typer + +from pathlib import Path +from typing import Optional + +from ..utils import log + +app = typer.Typer() + +@app.command() +def template_fn(): + """This is a sample function to be executed through the cli app + """ + log.info('Called the `template_fn` function') + doc_path: Path = Path(__file__).parent.parent.parent.parent / 'documentation' + + replace_keyword = '# Initialize' + + layout = doc_path / 'Layout.md' + usage = doc_path / 'Usage.ipynb' + + assert layout.is_file() + assert usage.is_file() + + print('Layout docs found. Updating...', end='') + + with open(usage, 'r') as f: + data = json.load(f) + + with open(layout, 'r') as f: + contents = f.read().splitlines() + contents = contents[:contents.index(replace_keyword)] + + for cell in data['cells']: + if cell.get('cell_type') == 'markdown': + contents.append(''.join(cell.get('source'))[1:]) + elif cell.get('cell_type') == 'code': + _contents = "\nUsage Examples:\n\n```python\n" + _contents += ''.join(cell.get('source')) + _contents += '\n```\n' + contents.append(_contents) + + with open(layout, 'w') as f: + f.write('\n'.join(contents)) + + print('Done.') \ No newline at end of file diff --git a/src/ak_sap/gui/gui_app.py b/src/ak_sap/gui/gui_app.py new file mode 100644 index 0000000..546f547 --- /dev/null +++ b/src/ak_sap/gui/gui_app.py @@ -0,0 +1,12 @@ +import typer + +from pathlib import Path +import subprocess +import sys + +app = typer.Typer() + +@app.command() +def launch(): + streamlit_filepath: Path = Path(__file__).parent.parent.parent.parent / 'Start_Here.py' + subprocess.run([f"{sys.executable}","-m", "streamlit", "run", str(streamlit_filepath)]) \ No newline at end of file diff --git a/src/ak_sap/misc/coordinates.py b/src/ak_sap/misc/coordinates.py index 62c275b..9174b32 100644 --- a/src/ak_sap/misc/coordinates.py +++ b/src/ak_sap/misc/coordinates.py @@ -6,5 +6,5 @@ class Coord: y: float z: float - def as_tuple(self) -> tuple[float]: + def as_tuple(self) -> tuple[float, float, float]: return (self.x, self.y, self.z) \ No newline at end of file diff --git a/src/ak_sap/utils/logger.py b/src/ak_sap/utils/logger.py index 5f55e65..c245a75 100644 --- a/src/ak_sap/utils/logger.py +++ b/src/ak_sap/utils/logger.py @@ -30,14 +30,14 @@ def info(self, msg): def warning(self, msg): try: - suffix = f'Warning in {Path(inspect.stack()[1].filename).name} -> {inspect.currentframe().f_back.f_code.co_name}: ' + suffix = f'Warning in {Path(inspect.stack()[1].filename).name} -> {inspect.currentframe().f_back.f_code.co_name}: ' # type: ignore except Exception: suffix = f'Warning in {Path(inspect.stack()[1].filename).name}: ' self.logger.warning(suffix + msg) def error(self, msg): try: - suffix = f'Error in {Path(inspect.stack()[1].filename).name} -> {inspect.currentframe().f_back.f_code.co_name}: ' + suffix = f'Error in {Path(inspect.stack()[1].filename).name} -> {inspect.currentframe().f_back.f_code.co_name}: ' # type: ignore except Exception: suffix = f'Error in {Path(inspect.stack()[1].filename).name}: ' self.logger.error(suffix + str(msg)) @@ -45,7 +45,7 @@ def error(self, msg): def critical(self, msg): try: - suffix = f'Critical in {Path(inspect.stack()[1].filename).name} -> {inspect.currentframe().f_back.f_code.co_name}: ' + suffix = f'Critical in {Path(inspect.stack()[1].filename).name} -> {inspect.currentframe().f_back.f_code.co_name}: ' # type: ignore except Exception: suffix = f'Error in {Path(inspect.stack()[1].filename).name}: ' self.logger.critical(suffix + str(msg)) diff --git a/src/ak_sap/wrapper.py b/src/ak_sap/wrapper.py index a9175c5..737055f 100644 --- a/src/ak_sap/wrapper.py +++ b/src/ak_sap/wrapper.py @@ -43,9 +43,8 @@ def __repr__(self) -> str: def __del__(self) -> None: try: # assert self.mySapObject.ApplicationExit(False) == 0 - # self.SapModel = None - # self.mySapObject = None - pass + self.SapModel = None + self.mySapObject = None except Exception as e: log.error(e.__str__()) @@ -99,6 +98,9 @@ def ishidden(self) -> bool: @property def version(self) -> str: return self.SapModel.GetVersion()[0] + + def exit(self, save: bool=False): + self.mySapObject.ApplicationExit(False) def model(attach_to_instance: bool, program_path: str|Path|None = None): """Returns SapObject.