Skip to content

Commit

Permalink
Merge pull request #406 from AequilibraE/develop
Browse files Browse the repository at this point in the history
Version 0.9 release
  • Loading branch information
jamiecook committed Apr 28, 2023
2 parents 1db977d + 7100cb7 commit 0459c54
Show file tree
Hide file tree
Showing 295 changed files with 9,698 additions and 10,065 deletions.
3 changes: 2 additions & 1 deletion .github/qgis_requirements.py
Expand Up @@ -10,4 +10,5 @@ def replace_in_file(file_path, text_orig, suffix):


replace_in_file("../requirements.txt", "numpy", "<1.22")
replace_in_file("../__version__.py", "{minor_version}", "+qgis")
replace_in_file("../pyproject.toml", "numpy", "<1.22")
replace_in_file("../__version__.py", "{minor_version}", ".dev0")
22 changes: 10 additions & 12 deletions .github/workflows/build_artifacts_qgis.yml
Expand Up @@ -12,15 +12,12 @@ jobs:
env:
HAS_SECRETS: ${{ secrets.AWS_SECRET_ACCESS_KEY != '' }}
CI_BUILD_QGIS_BINARY: "true"
continue-on-error: true
strategy:
matrix:
python-version: ['3.9']
python-version: ['3.9', '3.10']
architecture: ['x64']
os: [windows-latest, macos-latest]
include:
- os: macos-latest
CC: "gcc-12"
CXX: "g++-12"
steps:
- uses: actions/checkout@v3
- name: Set Python environment
Expand Down Expand Up @@ -54,20 +51,14 @@ jobs:

- name: Install dependencies
if: ${{matrix.os != 'macos-latest'}}
env:
CC: "gcc-12"
CXX: "g++-12"
run: |
python -m pip install --upgrade pip setuptools wheel twine
pip install -r requirements.txt
pip install -r requirements_additional.txt
- name: Build
if: ${{matrix.os != 'macos-latest'}}
env:
CC: "gcc-12"
CXX: "g++-12"
run: python setup.py bdist_wheel
run: python setup.py sdist bdist_wheel

- name: Stores artifacts along with the workflow result
uses: actions/upload-artifact@v3
Expand All @@ -82,3 +73,10 @@ jobs:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: twine upload dist/*.whl

- name: Publish tar files
if: ${{ (github.event_name == 'release') && (env.HAS_SECRETS == 'true') && (matrix.python-version == '3.9') }}
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: twine upload dist/*.gz
22 changes: 20 additions & 2 deletions .github/workflows/build_linux.yml
@@ -1,4 +1,4 @@
name: Build and upload Linux Python Package
name: Build and upload Linux Python Package (includes QGIS artifacts)

on: [pull_request, release]

Expand All @@ -7,6 +7,7 @@ jobs:
runs-on: ubuntu-20.04
env:
HAS_SECRETS: ${{ secrets.AWS_SECRET_ACCESS_KEY != '' }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
Expand All @@ -18,7 +19,7 @@ jobs:
python -m pip install --upgrade pip wheel cffi setuptools twine
- name: Build manylinux Python wheels
uses: RalfG/python-wheels-manylinux-build@v0.7.0
uses: RalfG/python-wheels-manylinux-build@v0.7.1
with:
python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311'
pip-wheel-args: '--no-deps'
Expand All @@ -28,6 +29,23 @@ jobs:
mkdir -p dist
cp -v ./*-manylinux*.whl dist/
- name: update versions to conform with QGIS
run: |
cd .github
python qgis_requirements.py
cd ..
- name: Build manylinux Python wheels
uses: RalfG/python-wheels-manylinux-build@v0.7.1
with:
python-versions: 'cp38-cp38 cp39-cp39 cp310-cp310'
pip-wheel-args: '--no-deps'

- name: Moves wheels
run: |
mkdir -p dist
cp -v ./*-manylinux*.whl dist/
- name: Stores artifacts along with the workflow result
if: ${{ github.event_name == 'push'}}
uses: actions/upload-artifact@v3
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build_mac.yml
Expand Up @@ -11,6 +11,7 @@ jobs:
HAS_SECRETS: ${{ secrets.AWS_SECRET_ACCESS_KEY != '' }}
CC: "gcc-12"
CXX: "g++-12"
continue-on-error: true
strategy:
matrix:
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11']
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build_windows.yml
Expand Up @@ -7,6 +7,7 @@ jobs:
runs-on: windows-2019
env:
HAS_SECRETS: ${{ secrets.AWS_SECRET_ACCESS_KEY != '' }}
continue-on-error: true
strategy:
matrix:
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11']
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/documentation.yml
Expand Up @@ -34,10 +34,21 @@ jobs:
- name: Compile library
run: |
python3 setup.py build_ext --inplace
python setup.py build_ext --inplace
- name: Check history of versions
run: python docs/website/check_documentation_versions.py
run: |
python docs/table_documentation.py
python docs/create_docs_data.py
python docs/website/check_documentation_versions.py
- name: Test docstrings
run: |
echo "DOCUMENTATION TESTING GOES HERE"
python -m doctest -v ./aequilibrae/parameters.py
python -m doctest -v ./aequilibrae/project/about.py
python -m doctest -v ./aequilibrae/project/zoning.py
- name: Build documentation
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -63,6 +63,7 @@ aequilibrae/**/*.html
aequilibrae/**/build/*
docs/build/*
docs/source/_generated/*
docs/source/project_database/*
docs/source/_auto_examples/*
*.cpp

Expand Down
4 changes: 2 additions & 2 deletions README.rst
Expand Up @@ -52,7 +52,7 @@ What is available
What is available only in QGIS
******************************

Some common resources for transportation modelling are inherently visual, and therefore they make more sense if
Some common resources for transportation modeling are inherently visual, and therefore they make more sense if
available within a GIS platform. For that reason, many resources are available only from AequilibraE's `QGIS plugin
<http://plugins.qgis.org/plugins/AequilibraE/>`_,
which uses AequilibraE as its computational workhorse and also provides GUIs for most of AequilibraE's tools. Said tool
Expand Down Expand Up @@ -85,7 +85,7 @@ Before there was AequilibraE, there was a need for something like AequilibraE ou
The very early days
*******************
It all started when I was a student at `UCI-ITS <www.its.uci.edu>`_ and needed low level access to outputs of standard
algorithms used in transportation modelling (e.g. path files from traffic assignment) and had that denied by the maker
algorithms used in transportation modeling (e.g. path files from traffic assignment) and had that denied by the maker
of the commercial software he normally used. There, the `first scratch of a traffic assignment procedure
<www.xl-optim.com/python-traffic-assignment>`_ was born.
After that, there were a couple of scripts developed to implement synthetic gravity models (calibration and application)
Expand Down
6 changes: 3 additions & 3 deletions __version__.py
@@ -1,5 +1,5 @@
version = 0.8
minor_version = "1"
release_name = "Rio de Janeiro"
version = 0.9
minor_version = "0"
release_name = "Queluz"

release_version = f"{version}.{minor_version}"
1 change: 0 additions & 1 deletion aequilibrae/__init__.py
Expand Up @@ -27,7 +27,6 @@
from aequilibrae.paths.graph import Graph
from aequilibrae.paths.assignment_paths import AssignmentPaths
from aequilibrae.project import Project
from aequilibrae.transit.gtfs import GTFS, create_gtfsdb
from aequilibrae.paths.results import AssignmentResults, SkimResults, PathResults

from aequilibrae import paths
Expand Down
141 changes: 71 additions & 70 deletions aequilibrae/distribution/gravity_application.py
Expand Up @@ -20,118 +20,119 @@


class GravityApplication:
"""Applies a synthetic gravity model
"""Applies a synthetic gravity model.
Model is an instance of SyntheticGravityModel class
Impedance is an instance of AequilibraEMatrix
Row and Column vectors are instances of AequilibraeData
Model is an instance of SyntheticGravityModel class.
Impedance is an instance of AequilibraEMatrix.
Row and Column vectors are instances of AequilibraeData.
::
.. code-block:: python
import pandas as pd
import sqlite3
>>> import pandas as pd
>>> from aequilibrae import Project
>>> from aequilibrae.matrix import AequilibraeMatrix, AequilibraeData
>>> from aequilibrae.distribution import SyntheticGravityModel, GravityApplication
from aequilibrae.matrix import AequilibraeMatrix
from aequilibrae.matrix import AequilibraeData
from aequilibrae.distribution import SyntheticGravityModel
from aequilibrae.distribution import GravityApplication
>>> project = Project.from_path("/tmp/test_project_ga")
# We define the model we will use
model = SyntheticGravityModel()
>>> model = SyntheticGravityModel()
# Before adding a parameter to the model, you need to define the model functional form
model.function = "GAMMA" # "EXPO" or "POWER"
>>> model.function = "GAMMA" # "EXPO" or "POWER"
# Only the parameter(s) applicable to the chosen functional form will have any effect
model.alpha = 0.1
model.beta = 0.0001
>>> model.alpha = 0.1
>>> model.beta = 0.0001
# Or you can load the model from a file
model.load('path/to/model/file')
# model.load('path/to/model/file')
# We load the impedance matrix
matrix = AequilibraeMatrix()
matrix.load('path/to/impedance_matrix.aem')
matrix.computational_view(['distance'])
>>> matrix = AequilibraeMatrix()
>>> matrix.load('/tmp/test_project_ga/matrices/skims.omx')
>>> matrix.computational_view(['distance_blended'])
# We create the vectors we will use
conn = sqlite3.connect('path/to/demographics/database')
query = "SELECT zone_id, population, employment FROM demographics;"
df = pd.read_sql_query(query,conn)
index = df.zone_id.values[:]
zones = index.shape[0]
>>> query = "SELECT zone_id, population, employment FROM zones;"
>>> df = pd.read_sql(query, project.conn)
>>> df.sort_values(by="zone_id", inplace=True)
# You create the vectors you would have
df = df.assign(production=df.population * 3.0)
df = df.assign(attraction=df.employment * 4.0)
>>> df = df.assign(production=df.population * 3.0)
>>> df = df.assign(attraction=df.employment * 4.0)
>>> zones = df.index.shape[0]
# We create the vector database
args = {"entries": zones, "field_names": ["productions", "attractions"],
"data_types": [np.float64, np.float64], "memory_mode": True}
vectors = AequilibraeData()
vectors.create_empty(**args)
>>> args = {"entries": zones, "field_names": ["productions", "attractions"],
... "data_types": [np.float64, np.float64], "memory_mode": True}
>>> vectors = AequilibraeData()
>>> vectors.create_empty(**args)
# Assign the data to the vector object
vectors.productions[:] = df.production.values[:]
vectors.attractions[:] = df.attraction.values[:]
vectors.index[:] = zones[:]
>>> vectors.productions[:] = df.production.values[:]
>>> vectors.attractions[:] = df.attraction.values[:]
>>> vectors.index[:] = df.zone_id.values[:]
# Balance the vectors
vectors.attractions[:] *= vectors.productions.sum() / vectors.attractions.sum()
>>> vectors.attractions[:] *= vectors.productions.sum() / vectors.attractions.sum()
# Create the problem object
args = {"impedance": matrix,
"rows": vectors,
"row_field": "productions",
"model": model,
"columns": vectors,
"column_field": "attractions",
"output": 'path/to/output/matrix.aem',
"nan_as_zero":True
}
gravity = GravityApplication(**args)
>>> args = {"impedance": matrix,
... "rows": vectors,
... "row_field": "productions",
... "model": model,
... "columns": vectors,
... "column_field": "attractions",
... "output": '/tmp/test_project_ga/matrices/matrix.aem',
... "nan_as_zero":True
... }
>>> gravity = GravityApplication(**args)
# Solve and save the outputs
gravity.apply()
gravity.output.export('path/to/omx_file.omx')
with open('path.to/report.txt', 'w') as f:
for line in gravity.report:
f.write(f'{line}\n')
>>> gravity.apply()
>>> gravity.output.export('/tmp/test_project_ga/matrices/omx_file.omx')
# To save your report into a file, you can do the following:
# with open('/tmp/test_project_ga/report.txt', 'w') as file:
# for line in gravity.report:
# file.write(f"{line}\\n")
"""

def __init__(self, project=None, **kwargs):
"""
Instantiates the Ipf problem
Args:
model (:obj:`SyntheticGravityModel`): Synthetic gravity model to apply
:Arguments:
**model** (:obj:`SyntheticGravityModel`): Synthetic gravity model to apply
impedance (:obj:`AequilibraeMatrix`): Impedance matrix to be used
**impedance** (:obj:`AequilibraeMatrix`): Impedance matrix to be used
rows (:obj:`AequilibraeData`): Vector object with data for row totals
**rows** (:obj:`AequilibraeData`): Vector object with data for row totals
row_field (:obj:`str`): Field name that contains the data for the row totals
**row_field** (:obj:`str`): Field name that contains the data for the row totals
columns (:obj:`AequilibraeData`): Vector object with data for column totals
**columns** (:obj:`AequilibraeData`): Vector object with data for column totals
column_field (:obj:`str`): Field name that contains the data for the column totals
**column_field** (:obj:`str`): Field name that contains the data for the column totals
project (:obj:`Project`, optional): The Project to connect to. By default, uses the currently active project
**project** (:obj:`Project`, optional): The Project to connect to. By default, uses the currently
active project
core_name (:obj:`str`, optional): Name for the output matrix core. Defaults to "gravity"
**core_name** (:obj:`str`, optional): Name for the output matrix core. Defaults to "gravity"
parameters (:obj:`str`, optional): Convergence parameters. Defaults to those in the parameter file
**parameters** (:obj:`str`, optional): Convergence parameters. Defaults to those in the parameter file
nan_as_zero (:obj:`bool`, optional): If Nan values should be treated as zero. Defaults to True
**nan_as_zero** (:obj:`bool`, optional): If Nan values should be treated as zero. Defaults to True
Results:
output (:obj:`AequilibraeMatrix`): Result Matrix
:Results:
**output** (:obj:`AequilibraeMatrix`): Result Matrix
report (:obj:`list`): Iteration and convergence report
**report** (:obj:`list`): Iteration and convergence report
error (:obj:`str`): Error description
**error** (:obj:`str`): Error description
"""

self.project = project
Expand Down Expand Up @@ -224,10 +225,10 @@ def apply(self):
def save_to_project(self, name: str, file_name: str, project=None) -> None:
"""Saves the matrix output to the project file
Args:
name (:obj:`str`): Name of the desired matrix record
file_name (:obj:`str`): Name for the matrix file name. AEM and OMX supported
project (:obj:`Project`, Optional): Project we want to save the results to. Defaults to the active project
:Arguments:
**name** (:obj:`str`): Name of the desired matrix record
**file_name** (:obj:`str`): Name for the matrix file name. AEM and OMX supported
**project** (:obj:`Project`, Optional): Project we want to save the results to. Defaults to the active project
"""

project = project or get_active_project()
Expand Down

0 comments on commit 0459c54

Please sign in to comment.