From 675a815b485ee75bd85f09def008e233980ecf65 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 00:43:30 -0400 Subject: [PATCH 01/13] refactor for pypi --- engforge/dynamics.py | 1 - engforge/problem_context.py | 2 -- pyproject.toml | 32 ++++++++++++++++++-------------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/engforge/dynamics.py b/engforge/dynamics.py index 2b94abd..cec79a3 100644 --- a/engforge/dynamics.py +++ b/engforge/dynamics.py @@ -29,7 +29,6 @@ from collections import OrderedDict import numpy as np import pandas -import expiringdict import attr, attrs diff --git a/engforge/problem_context.py b/engforge/problem_context.py index a26de9d..72ba712 100644 --- a/engforge/problem_context.py +++ b/engforge/problem_context.py @@ -62,8 +62,6 @@ from collections import OrderedDict import numpy as np import pandas as pd -import expiringdict -import attr, attrs import datetime diff --git a/pyproject.toml b/pyproject.toml index 4840542..965f100 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,8 +21,6 @@ classifiers = [ ] requires-python = ">=3.9" dependencies = [ - "virtualenv", - "wheel", "attrs~=23.2.0", "arrow", "deepdiff>=6.3.1", @@ -34,18 +32,6 @@ dependencies = [ "scikit-learn~=1.3.2", "seaborn", "tabulate", - "sectionproperties~=3.1.2", - "PyNiteFEA @ git+https://github.com/Ottermatics/PyNite.git@v2nept", - "control", - "fluids", - "CoolProp", - "expiringdict~=1.2.2", - "zipp==3.1.0", - "jinja2>=2.11.3", - "freezegun>=0.3.12", - "sphinx>=3.2.1", - "recommonmark>=0.6.0", - "sphinx-rtd-theme>=1.0.0", "networkx-query==1.0.1", "networkx>=2.5.1", "black~=24.8.0", @@ -62,6 +48,23 @@ Repository = "https://github.com/Ottermatics/engforge" Documentation = "https://ottermatics.github.io/engforge/" [project.optional-dependencies] + +dev = [ + "wheel", + "jinja2>=2.11.3", + "sphinx>=3.2.1", + "recommonmark>=0.6.0", + "sphinx-rtd-theme>=1.0.0", +] + +eng = [ + "control", + "fluids", + "CoolProp", + "sectionproperties~=3.1.2", + "PyNiteFEA" +] + database = [ "SQLAlchemy~=1.3.17", "SQLAlchemy-Utils", @@ -70,6 +73,7 @@ database = [ "sqlalchemy-batch-inserts", ] google = [ + "expiringdict~=1.2.2", "pygsheets==2.0.4", "pydrive2==1.8.1", ] From 605608e84856c9a1b815fddb1a1cd04394b2bea5 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 01:09:41 -0400 Subject: [PATCH 02/13] Refactor GitHub Actions workflow to separate formatting and testing jobs --- .github/workflows/build.yml | 59 ++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c43265e..87951b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,31 +19,26 @@ on: workflow_dispatch: workflow_call: jobs: - test: + format: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: [ "3.9", "3.10", "3.11" ] - + if: github.event_name == 'pull_request' + permissions: + contents: write steps: - uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - + ref: ${{ github.head_ref || github.ref }} + - name: Install Python uses: actions/setup-python@v4 with: - python-version: ${{ matrix.python-version }} + python-version: "3.11" - - name: Install dependencies + - name: Install Black run: | python -m pip install --upgrade pip - pip install flake8 pytest black - pip install -e .[all] - - - name: Display Python Version - run: python -c "import sys; print(sys.version)" + pip install black - name: Check for existing release if: github.event_name == 'pull_request' @@ -63,11 +58,8 @@ jobs: else echo "Version v$VERSION is available" fi - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Auto-format with Black - if: github.event_name == 'pull_request' run: | black ./engforge if ! git diff --quiet; then @@ -75,8 +67,39 @@ jobs: git config --local user.name "GitHub Action" git add -A git commit -m "Auto-format code with Black [skip ci]" || exit 0 - git push + git push origin HEAD:${{ github.head_ref }} fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test: + runs-on: ubuntu-latest + needs: [format] + if: always() && (needs.format.result == 'success' || needs.format.result == 'skipped') + strategy: + fail-fast: false + matrix: + python-version: [ "3.9", "3.10", "3.11" ] + + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref || github.ref }} + + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest black + pip install -e .[all] + + - name: Display Python Version + run: python -c "import sys; print(sys.version)" - name: Run tests run: python -m unittest discover -s engforge/test -p "test_*.py" -v From 485820f1fe3cc493198ec35778ebb32496b29a67 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 13:04:37 -0400 Subject: [PATCH 03/13] Bump version to 0.2.1 in pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 965f100..de3b05f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "engforge" -version = "0.2.0" +version = "0.2.1" description = "The Engineer's Framework" readme = "README.md" license = "MIT" authors = [ - {name = "Kevin Russell", email = "kevin@ottermatics.com"} + {name = "Kevin Russell", email = "olly@ottermatics.com"} ] classifiers = [ "Development Status :: 3 - Alpha", From 2cb7a80d3c27a8f8ca255352d6d103cd826a476f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 11 Sep 2025 17:04:59 +0000 Subject: [PATCH 04/13] Auto-format code with Black [skip ci] --- engforge/attr_plotting.py | 3 +- engforge/attributes.py | 2 +- engforge/component_collections.py | 4 +-- engforge/datastores/__init__.py | 49 ++++++++++++++++++++----------- engforge/dynamics.py | 2 +- engforge/eng/costs.py | 6 ++-- engforge/eng/pipes.py | 2 +- engforge/env_var.py | 2 +- engforge/problem_context.py | 10 +++---- engforge/solver.py | 6 ++-- engforge/test/test_modules.py | 1 - engforge/typing.py | 4 +-- 12 files changed, 51 insertions(+), 40 deletions(-) diff --git a/engforge/attr_plotting.py b/engforge/attr_plotting.py index 8762cdd..3295189 100644 --- a/engforge/attr_plotting.py +++ b/engforge/attr_plotting.py @@ -1,5 +1,4 @@ -"""This module defines Plot and Trace methods that allow the plotting of Statistical & Transient relationships of data in each system -""" +"""This module defines Plot and Trace methods that allow the plotting of Statistical & Transient relationships of data in each system""" import attrs import uuid diff --git a/engforge/attributes.py b/engforge/attributes.py index 1853384..f35f7f8 100644 --- a/engforge/attributes.py +++ b/engforge/attributes.py @@ -1,6 +1,6 @@ """Defines a customizeable attrs attribute that is handled in configuration, -on init an instance of `Instance` type for any ATTR_BASE subclass is created """ +on init an instance of `Instance` type for any ATTR_BASE subclass is created""" import attrs, attr, uuid from engforge.logging import LoggingMixin, log diff --git a/engforge/component_collections.py b/engforge/component_collections.py index ff83bfa..bef2736 100644 --- a/engforge/component_collections.py +++ b/engforge/component_collections.py @@ -1,12 +1,12 @@ """define a collection of components that will propigate to its parents dataframe -When `wide` is set each component's references are reported to the system's table, otherwise only one component's references are reported, however the system will iterate over the components by calling `system.iterable_components` +When `wide` is set each component's references are reported to the system's table, otherwise only one component's references are reported, however the system will iterate over the components by calling `system.iterable_components` Define a Iterable Component slot in a system by calling `Slot.define_iterable(...,wide=True/False)` CostModel isonly supported in wide mode at this time. -Types: +Types: 1. ComponentList, ordered by index 2. ComponentDict, ordered by key 3. ComponentGraph, ?#TODO: diff --git a/engforge/datastores/__init__.py b/engforge/datastores/__init__.py index 8867cd7..4c8b544 100644 --- a/engforge/datastores/__init__.py +++ b/engforge/datastores/__init__.py @@ -4,6 +4,7 @@ import importlib.util from pathlib import Path + def get_project_root(): """Find the project root containing pyproject.toml""" current = Path(__file__).parent @@ -13,6 +14,7 @@ def get_project_root(): current = current.parent return None + def install_optional_dependencies(group_name="database"): """Install optional dependencies for a specific group""" project_root = get_project_root() @@ -20,32 +22,34 @@ def install_optional_dependencies(group_name="database"): print("Could not find pyproject.toml. Manual installation required:") print(f"pip install engforge[{group_name}]") return False - + try: # Try to import tomllib (Python 3.11+) or tomli (fallback) try: import tomllib except ImportError: import tomli as tomllib - + # Read pyproject.toml with open(project_root / "pyproject.toml", "rb") as f: config = tomllib.load(f) - + optional_deps = config.get("project", {}).get("optional-dependencies", {}) if group_name not in optional_deps: print(f"No optional dependency group '{group_name}' found") return False - + # Install the optional dependencies package_name = config.get("project", {}).get("name", "engforge") install_spec = f"{package_name}[{group_name}]" - + print(f"Installing {install_spec}...") - result = subprocess.run([ - sys.executable, "-m", "pip", "install", install_spec - ], capture_output=True, text=True) - + result = subprocess.run( + [sys.executable, "-m", "pip", "install", install_spec], + capture_output=True, + text=True, + ) + if result.returncode == 0: print(f"✅ Successfully installed {install_spec}") return True @@ -53,31 +57,41 @@ def install_optional_dependencies(group_name="database"): print(f"❌ Failed to install {install_spec}") print(f"Error: {result.stderr}") return False - + except Exception as e: print(f"Error during auto-installation: {e}") print(f"Please install manually: pip install engforge[{group_name}]") return False + def check_and_install_datastores(): """Check if datastores dependencies are available, auto-install if needed""" try: import engforge.datastores.data + return True except ImportError as e: print(f"Datastores dependencies not available: {e}") print("") - + # Check if we're in development mode (editable install) and interactive project_root = get_project_root() - is_interactive = hasattr(sys, 'ps1') or sys.stdout.isatty() - - if project_root and (project_root / "pyproject.toml").exists() and is_interactive: + is_interactive = hasattr(sys, "ps1") or sys.stdout.isatty() + + if ( + project_root + and (project_root / "pyproject.toml").exists() + and is_interactive + ): try: - answer = input("Auto-install database dependencies? (y/N): ").strip().lower() - if answer in ['y', 'yes']: + answer = ( + input("Auto-install database dependencies? (y/N): ").strip().lower() + ) + if answer in ["y", "yes"]: if install_optional_dependencies("database"): - print("Please restart your Python session to use the newly installed dependencies.") + print( + "Please restart your Python session to use the newly installed dependencies." + ) return False else: print("Auto-installation failed. Install manually with:") @@ -96,6 +110,7 @@ def check_and_install_datastores(): print("pip install engforge[database]") return False + # Auto-check and install on import if not check_and_install_datastores(): pass # Dependencies not available, but user has been informed diff --git a/engforge/dynamics.py b/engforge/dynamics.py index cec79a3..2b40058 100644 --- a/engforge/dynamics.py +++ b/engforge/dynamics.py @@ -8,7 +8,7 @@ #TODO: The top level system will collect the underlying dynamical systems and combine them to an index and overall state space model. This will allow for the creation of a system of systems, and the ability to create a system of systems with a single state space model. -#TODO: integration is done by the solver, where DynamicSystems have individual solver control, solver control is set for a smart default scipy +#TODO: integration is done by the solver, where DynamicSystems have individual solver control, solver control is set for a smart default scipy """ from engforge.configuration import Configuration, forge diff --git a/engforge/eng/costs.py b/engforge/eng/costs.py index c913d1c..7210b7b 100644 --- a/engforge/eng/costs.py +++ b/engforge/eng/costs.py @@ -4,14 +4,14 @@ CostModel's can have cost_property's which detail how and when a cost should be applied & grouped. By default each CostModel has a `cost_per_item` which is reflected in `item_cost` cost_property set on the `initial` term as a `unit` category. Multiple categories of cost are also able to be set on cost_properties as follows -Cost Models can represent multiple instances of a component, and can be set to have a `num_items` multiplier to account for multiple instances of a component. CostModels can have a `term_length` which will apply costs over the term, using the `cost_property.mode` to determine at which terms a cost should be applied. +Cost Models can represent multiple instances of a component, and can be set to have a `num_items` multiplier to account for multiple instances of a component. CostModels can have a `term_length` which will apply costs over the term, using the `cost_property.mode` to determine at which terms a cost should be applied. ``` @forge class Widget(Component,CostModel): - + #use num_items as a multiplier for costs, `cost_properties` can have their own custom num_items value. - num_items:float = 100 + num_items:float = 100 @cost_property(mode='initial',category='capex,manufacturing',num_items=1) def cost_of_XYZ(self) -> float: diff --git a/engforge/eng/pipes.py b/engforge/eng/pipes.py index eac9ea4..4953441 100644 --- a/engforge/eng/pipes.py +++ b/engforge/eng/pipes.py @@ -2,7 +2,7 @@ start with single phase and move to others -1) Pumps Power = C x Q x P +1) Pumps Power = C x Q x P 2) Compressor = C x (QP) x (PR^C2 - 1) 3) Pipes = dP = C x fXL/D x V^2 / 2g 4) Pipe Fittings / Valves = dP = C x V^2 (fXL/D +K) | K is a constant, for valves it can be interpolated between closed and opened diff --git a/engforge/env_var.py b/engforge/env_var.py index 77212df..5519420 100644 --- a/engforge/env_var.py +++ b/engforge/env_var.py @@ -2,7 +2,7 @@ A global record of variables is kept for informational purposes in keeping track of progam variables -To prevent storage of env vars in program memory, access to the os env variables is provided on access of the `secret` variable. It is advisable to use the result of this as directly as possible when dealing with actual secrets. +To prevent storage of env vars in program memory, access to the os env variables is provided on access of the `secret` variable. It is advisable to use the result of this as directly as possible when dealing with actual secrets. For example add: `db_driver(DB_HOST.secret,DB_PASSWORD.secret,...) """ diff --git a/engforge/problem_context.py b/engforge/problem_context.py index 72ba712..6363586 100644 --- a/engforge/problem_context.py +++ b/engforge/problem_context.py @@ -1,6 +1,6 @@ -"""The ProblemExec provides a uniform set of options for managing the state of the system and its solvables, establishing the selection of combos or de/active attributes to Solvables. Once once created any further entracnces to ProblemExec will return the same instance until finally the last exit is called. +"""The ProblemExec provides a uniform set of options for managing the state of the system and its solvables, establishing the selection of combos or de/active attributes to Solvables. Once once created any further entracnces to ProblemExec will return the same instance until finally the last exit is called. -The ProblemExec class allows entrance to a its context to the same instance until finally the last exit is called. The first entrance to the context will create the instance, each subsequent entrance will return the same instance. The ProblemExec arguments are set the first time and remove keyword arguments from the input dictionary (passed as a dict ie stateful) to subsequent methods. +The ProblemExec class allows entrance to a its context to the same instance until finally the last exit is called. The first entrance to the context will create the instance, each subsequent entrance will return the same instance. The ProblemExec arguments are set the first time and remove keyword arguments from the input dictionary (passed as a dict ie stateful) to subsequent methods. This isn't technically a singleton pattern, but it does provide a similar interface. Instead mutliple problem instances will be clones of the first instance, with the optional difference of input/output/event criteria. The first instance will be returned by each context entry, so for that reason it may always appear to have same instance, however each instance is unique in a recusive setting so it may record its own state and be reverted to its own state as per the options defined. #TODO: allow update of kwargs on re-entrance @@ -13,9 +13,9 @@ pe._sys_refs #get the references and compiled problem for i in range(10): pe.solve_min(pe.Xref,pe.Yref,**other_args) - pe.set_checkpoint() #save the state of the system + pe.set_checkpoint() #save the state of the system pe.save_data() - + #Solver Module (can use without knowledge of the runtime system) with ProblemExec(sys,{},Xnew=Xnext,ctx_fail_new=True) as pe: @@ -31,7 +31,7 @@ # Active Mode Handiling The `only_active` argument can be used to select only active items. The `activate` and `deactivate` arguments can be used to activate or deactivate specific solvables. -`add_obj` can be used to add an objective to the solver. +`add_obj` can be used to add an objective to the solver. # Root Parameter Determination: Any session's may access root session parameters defined in the `root_parameters` dictionary like `converged`, or `data` can be accessed by calling `session.` this eventually calls `root._` via the __getattr__ method. diff --git a/engforge/solver.py b/engforge/solver.py index bb64337..812abff 100644 --- a/engforge/solver.py +++ b/engforge/solver.py @@ -4,12 +4,12 @@ ### A general Solver Run Will Look Like: 0. run pre execute (signals=pre,both) -1. add execution context with **kwargument for the signals. +1. add execution context with **kwargument for the signals. 2. parse signals here (through a new Signals.parse_rtkwargs(**kw)) which will non destructively parse the signals and return all the signal candiates which are put into an ProblemExec object that resets the signals after the run depending on the revert behavior 3. the execute method will recieve this ProblemExec object where it can update the solver references / signals so that it can handle them per the signals api -4. with self.execution_context(**kwargs) as ctx_exe: +4. with self.execution_context(**kwargs) as ctx_exe: 1. pre-update / signals - #self.execute(ctx_exe,**kwargs) + #self.execute(ctx_exe,**kwargs) 2. post-update / signals > signals will be reset after the execute per the api 5. run post update diff --git a/engforge/test/test_modules.py b/engforge/test/test_modules.py index 4f518e8..6b01692 100644 --- a/engforge/test/test_modules.py +++ b/engforge/test/test_modules.py @@ -21,7 +21,6 @@ import time - class ImportTest(unittest.TestCase): """We test the compilation of all included modules""" diff --git a/engforge/typing.py b/engforge/typing.py index 9109207..c536adb 100644 --- a/engforge/typing.py +++ b/engforge/typing.py @@ -1,6 +1,4 @@ -""" - -""" +""" """ import pandas, attr, numpy from engforge.properties import * From 03cdc753a2f952530bc2ae092db126a823aa7ff9 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 15:59:13 -0400 Subject: [PATCH 05/13] Update quarterly lambda to include econ parameter and change log level to warning for inherited attribute overrides --- engforge/_testing_components.py | 2 +- engforge/configuration.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/engforge/_testing_components.py b/engforge/_testing_components.py index cf43fa8..214a064 100644 --- a/engforge/_testing_components.py +++ b/engforge/_testing_components.py @@ -760,7 +760,7 @@ class Comp2(Norm, CostModel): comp1 = Slot.define(Comp1, none_ok=True, default_ok=False) -quarterly = lambda inst, term: True if (term + 1) % 3 == 0 else False +quarterly = lambda inst, term, econ: True if (term + 1) % 3 == 0 else False @forge diff --git a/engforge/configuration.py b/engforge/configuration.py index add80b4..2ec6441 100644 --- a/engforge/configuration.py +++ b/engforge/configuration.py @@ -331,11 +331,12 @@ def signals_slots_handler( f"{cls.__name__} overriding inherited attr: {o.name} as a system property overriding it" ) elif o.inherited and k in cls_dict: - log.debug( + log.warning( f"{cls.__name__} overriding inherited attr: {o.name} as {cls_dict[k]} in cls" ) # FIXME: should we deepcopy? - cls.__anony_store[k] = sscb = lambda: copy.copy(cls_dict[k]) + val = copy.copy(cls_dict[k]) + cls.__anony_store[k] = sscb = lambda: val out.append(o.evolve(default=attrs.Factory(sscb))) else: log.msg(f"{cls.__name__} adding attr: {o.name}") From 3337fd1eea710d4bcfc05a2ca11887187c97ccb7 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:01:41 -0400 Subject: [PATCH 06/13] Update all optional dependencies in pyproject.toml to include 'eng' and 'dev' --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index de3b05f..f73b3ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,7 @@ docs = [ "toml~=0.10.2", ] all = [ - "engforge[database,google,cloud,distributed,docs]", + "engforge[database,google,cloud,distributed,docs,eng,dev]", ] [tool.setuptools] From 7e12f7e6b5481f57b5a04d3e227fd8fea7c8904b Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:10:02 -0400 Subject: [PATCH 07/13] Enhance build workflow to display Python version and list specific packages --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87951b1..6eaeb1f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,10 @@ jobs: pip install -e .[all] - name: Display Python Version - run: python -c "import sys; print(sys.version)" + run: | + python -c "import sys; print(sys.version)" + python -m pip list | grep PyNite + python -m pip list | grep CoolProp - name: Run tests run: python -m unittest discover -s engforge/test -p "test_*.py" -v From 7e3d1d29980b01d23ae922b7d17136dc4a10b364 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:10:56 -0400 Subject: [PATCH 08/13] fmt rmv check --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6eaeb1f..7766b67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,6 +106,3 @@ jobs: - name: Run tests run: python -m unittest discover -s engforge/test -p "test_*.py" -v - - - name: Verify Black formatting - run: black --check --verbose ./engforge \ No newline at end of file From 521e41f0a7fd5ac4c46952d60700541bc87f3a3c Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:27:59 -0400 Subject: [PATCH 09/13] Fix case sensitivity in package check for CoolProp in build workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7766b67..1af91c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -102,7 +102,7 @@ jobs: run: | python -c "import sys; print(sys.version)" python -m pip list | grep PyNite - python -m pip list | grep CoolProp + python -m pip list | grep coolprop - name: Run tests run: python -m unittest discover -s engforge/test -p "test_*.py" -v From 5194b9b1b1d259ad539bf6e295d8222b6ec5992b Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:41:18 -0400 Subject: [PATCH 10/13] Fix casing for Pynite imports and update dependencies in pyproject.toml --- .github/workflows/build.yml | 8 +++----- engforge/eng/geometry.py | 2 +- engforge/eng/solid_materials.py | 4 ++-- engforge/eng/structure.py | 10 +++++----- engforge/eng/structure_beams.py | 4 ++-- pyproject.toml | 4 ++-- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1af91c2..88dc52e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,11 +98,9 @@ jobs: pip install flake8 pytest black pip install -e .[all] - - name: Display Python Version + - name: Check Python & Run Tests run: | python -c "import sys; print(sys.version)" - python -m pip list | grep PyNite + python -m pip list | grep PyniteFEA python -m pip list | grep coolprop - - - name: Run tests - run: python -m unittest discover -s engforge/test -p "test_*.py" -v + python -m unittest discover -s engforge/test -p "test_*.py" -v diff --git a/engforge/eng/geometry.py b/engforge/eng/geometry.py index f375ede..65aaf9f 100644 --- a/engforge/eng/geometry.py +++ b/engforge/eng/geometry.py @@ -1,4 +1,4 @@ -"""These exist as in interface to sectionproperties from PyNite""" +"""These exist as in interface to sectionproperties from Pynite""" from engforge.configuration import Configuration, forge, LoggingMixin from engforge.properties import ( diff --git a/engforge/eng/solid_materials.py b/engforge/eng/solid_materials.py index 62f9f8d..aca4e79 100644 --- a/engforge/eng/solid_materials.py +++ b/engforge/eng/solid_materials.py @@ -11,7 +11,7 @@ import json, hashlib # One Material To Merge Them All -from PyNite import Material as PyNiteMat +from Pynite import Material as PyniteMat from sectionproperties.pre.pre import Material as SecMat SectionMaterial = SecMat.mro()[0] @@ -35,7 +35,7 @@ def ih(val): @forge(hash=False) -class SolidMaterial(SectionMaterial, PyNiteMat.Material, Configuration): +class SolidMaterial(SectionMaterial, PyniteMat.Material, Configuration): """A class to hold physical properties of solid structural materials and act as both a section property material and a pynite material""" __metaclass__ = SecMat diff --git a/engforge/eng/structure.py b/engforge/eng/structure.py index 62dc6d2..c2315f1 100644 --- a/engforge/eng/structure.py +++ b/engforge/eng/structure.py @@ -28,7 +28,7 @@ import sectionproperties import sectionproperties.pre.geometry as geometry import sectionproperties.pre.library.primitive_sections as sections -import PyNite as pynite +import Pynite as pynite import numpy as np from scipy import optimize as sciopt import pandas as pd @@ -208,7 +208,7 @@ class BeamDict(ComponentDict): # TODO: Make analysis, where each load case is a row, but how sytactically? @forge class Structure(System, CostModel, PredictionMixin): - """A integration between sectionproperties and PyNite, with a focus on ease of use + """A integration between sectionproperties and Pynite, with a focus on ease of use Right now we just need an integration between Sections+Materials and Members, to find, CG, and inertial components @@ -658,7 +658,7 @@ def quad_info(self) -> dict: return self._quad_info.copy() def add_mesh(self, meshtype, *args, **kwargs): - """maps to appropriate PyNite mesh generator but also applies loads + """maps to appropriate Pynite mesh generator but also applies loads Currently these types are supported ['rect','annulus','cylinder','triangle'] """ @@ -951,7 +951,7 @@ def costs(self) -> dict: return out def visulize(self, **kwargs): - from PyNite import Visualization + from Pynite import Visualization if "combo_name" not in kwargs: kwargs["combo_name"] = self.default_combo @@ -1054,7 +1054,7 @@ def merge_displacement_results(self, combo, D): node.RZ[combo] = D[node.ID * 6 + 5, 0] def prep_remote_analysis(self): - """copies PyNite analysis pre-functions""" + """copies Pynite analysis pre-functions""" # Generate all meshes for mesh in self.frame.Meshes.values(): if mesh.is_generated == False: diff --git a/engforge/eng/structure_beams.py b/engforge/eng/structure_beams.py index a6a7056..920229f 100644 --- a/engforge/eng/structure_beams.py +++ b/engforge/eng/structure_beams.py @@ -26,9 +26,9 @@ import sectionproperties.pre.geometry as geometry import sectionproperties.pre.library.primitive_sections as sections import sectionproperties.analysis.section as cross_section -import PyNite as pynite +import Pynite as pynite -# from PyNite import Visualization +# from Pynite import Visualization import copy nonetype = type(None) diff --git a/pyproject.toml b/pyproject.toml index f73b3ad..0aca512 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "engforge" -version = "0.2.1" +version = "0.3.0" description = "The Engineer's Framework" readme = "README.md" license = "MIT" @@ -62,7 +62,7 @@ eng = [ "fluids", "CoolProp", "sectionproperties~=3.1.2", - "PyNiteFEA" + "PyniteFEA>=1.2.0" ] database = [ From 03439a6806c9a7c7d9605d2d8fe28baa3d76f927 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:47:27 -0400 Subject: [PATCH 11/13] Update package checks in build workflow to handle missing dependencies gracefully --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88dc52e..fa9962c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,6 +101,6 @@ jobs: - name: Check Python & Run Tests run: | python -c "import sys; print(sys.version)" - python -m pip list | grep PyniteFEA - python -m pip list | grep coolprop + python -m pip list | { grep -v PyNiteFEA || pynite-not-found } + python -m pip list | { grep -v coolprop || coolprop-not-found } python -m unittest discover -s engforge/test -p "test_*.py" -v From a43b14b9ed573db66ffdd92d8ef660f988fe7467 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:50:45 -0400 Subject: [PATCH 12/13] Improve error handling in package checks by echoing not found messages for PyNiteFEA and CoolProp --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa9962c..efa86eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,6 +101,6 @@ jobs: - name: Check Python & Run Tests run: | python -c "import sys; print(sys.version)" - python -m pip list | { grep -v PyNiteFEA || pynite-not-found } - python -m pip list | { grep -v coolprop || coolprop-not-found } + python -m pip list | { grep -v PyNiteFEA || echo "pynite-not-found" } + python -m pip list | { grep -v coolprop || echo "coolprop-not-found" } python -m unittest discover -s engforge/test -p "test_*.py" -v From 87662e31ac1dbc22f7caf729b508c78e69d4172d Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 11 Sep 2025 16:57:17 -0400 Subject: [PATCH 13/13] Refactor package checks to simplify grep commands for PyNiteFEA and CoolProp --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efa86eb..35a1775 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,6 +101,6 @@ jobs: - name: Check Python & Run Tests run: | python -c "import sys; print(sys.version)" - python -m pip list | { grep -v PyNiteFEA || echo "pynite-not-found" } - python -m pip list | { grep -v coolprop || echo "coolprop-not-found" } + python -m pip list | grep PyNiteFEA || echo "pynite-not-found" + python -m pip list | grep coolprop || echo "coolprop-not-found" python -m unittest discover -s engforge/test -p "test_*.py" -v