Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[flake8]
# E203, W503: black and flake8 disagree on whitespace/operator placement
ignore = E203, W503
max-line-length = 88
exclude = build, dist, .eggs
152 changes: 152 additions & 0 deletions .github/workflows/notebooks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# GitHub Actions workflow for executing Jupyter notebooks
# Runs on pull requests and nightly to ensure notebooks work correctly

name: Notebooks

on:
push:
branches: [main, master]
paths:
- 'notebooks/**'
- 'boxcrete/**'
- 'pyproject.toml'
- '.github/workflows/notebooks.yml'
pull_request:
branches: [main, master]
paths:
- 'notebooks/**'
- 'boxcrete/**'
- 'pyproject.toml'
- '.github/workflows/notebooks.yml'
schedule:
# Run nightly at 3:00 AM UTC
- cron: '0 3 * * *'
workflow_dispatch:

jobs:
execute-notebooks:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10']

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
${{ runner.os }}-pip-

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[notebooks]"

- name: Install Jupyter kernel
run: |
python -m ipykernel install --user --name python3

- name: Execute notebooks
run: |
python - << 'PYEOF'
import subprocess, sys
from pathlib import Path

notebooks = sorted(Path("notebooks").glob("*.ipynb"))
failed = []

for nb in notebooks:
print(f"{'=' * 40}")
print(f"Executing: {nb}")
print(f"{'=' * 40}")

result = subprocess.run(
[
sys.executable, "-m", "jupyter", "nbconvert",
"--to", "notebook",
"--execute",
"--inplace",
"--ExecutePreprocessor.timeout=600",
"--ExecutePreprocessor.kernel_name=python3",
str(nb),
],
capture_output=False,
)

if result.returncode != 0:
failed.append(str(nb))
print(f"❌ Failed: {nb}")
else:
print(f"✅ Successfully executed: {nb}")
print()

if failed:
print(f"\n{len(failed)} notebook(s) failed:")
for f in failed:
print(f" - {f}")
sys.exit(1)
else:
print(f"\nAll {len(notebooks)} notebook(s) executed successfully.")
PYEOF

- name: Upload executed notebooks as artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: executed-notebooks
path: notebooks/*.ipynb
retention-days: 7

notebook-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install nbformat

- name: Validate notebook format
run: |
python - << 'EOF'
import nbformat
import sys
from pathlib import Path

notebooks = list(Path('notebooks').glob('*.ipynb'))
errors = []

for nb_path in notebooks:
try:
with open(nb_path, 'r', encoding='utf-8') as f:
nb = nbformat.read(f, as_version=4)
print(f"✅ Valid notebook: {nb_path}")
except Exception as e:
errors.append(f"❌ Invalid notebook {nb_path}: {e}")
print(errors[-1])

if errors:
print(f"\n{len(errors)} notebook(s) failed validation.")
sys.exit(1)
else:
print(f"\nAll {len(notebooks)} notebook(s) are valid.")
EOF
85 changes: 85 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# GitHub Actions workflow for running unit tests
# Runs on pull requests and nightly

name: Tests

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
schedule:
# Run nightly at 2:00 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12']

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
${{ runner.os }}-pip-

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Run unit tests with pytest
run: |
python -m pytest test/ -v --tb=short --cov=boxcrete --cov-report=xml --cov-report=term-missing --cov-fail-under=100

- name: Upload coverage reports
uses: codecov/codecov-action@v4
if: matrix.python-version == '3.10'
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false

lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install linting dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 black

- name: Check code formatting with black
run: |
black --check --diff .

- name: Lint with flake8
run: |
# Stop build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# Exit-zero treats all errors as warnings
flake8 . --count --exit-zero --statistics
60 changes: 60 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Jupyter Notebooks
.ipynb_checkpoints/
*/.ipynb_checkpoints/

# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
.nox/
coverage.xml
*.cover
.hypothesis/

# Virtual environments
.env
.venv
env/
venv/
ENV/

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Distribution
*.tar.gz
*.zip

# Planning files
*.plan.md
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Sustainable Concrete via Bayesian Optimization

Concrete is responsbile for up to **8% of anthropogenic carbon dioxide emissions per year** - compared to less than 3% for all air travel - and urgently needs to be decarbonized in order to achieve a sustainable future.
Concrete is responsible for up to **8% of anthropogenic carbon dioxide emissions per year** - compared to less than 3% for all air travel - and urgently needs to be decarbonized in order to achieve a sustainable future.
We invite researchers and practitioners of both machine learning and civil engineering
to collaborate on discovering more sustainable concrete formulations that are applicable
to a wide array of construction projects, at scale.
Expand All @@ -13,7 +13,38 @@ This repository contains probabilistic models and data for the

as a function of their composition, consisting of
cement, slag, water, to name a few basic ingredients.
See the `models.py` file for implementation details.
See `boxcrete/models.py` for implementation details.

## Installation

Install directly from GitHub (no cloning required):
```bash
pip install git+https://github.com/facebookresearch/SustainableConcrete.git
```

Or install from source for development:
```bash
git clone https://github.com/facebookresearch/SustainableConcrete.git
cd SustainableConcrete
pip install -e .
```

For development (includes testing and linting tools):
```bash
pip install -e ".[dev]"
```

For running notebooks:
```bash
pip install -e ".[notebooks]"
```

## Usage

```python
from boxcrete.models import SustainableConcreteModel
from boxcrete.utils import load_concrete_strength, get_mortar_bounds
```

The models can be used for a variety of tasks, including but not limited to
1) continuous-time strength predictions with uncertainty bands for a user-specified concrete mix, and
Expand Down
43 changes: 43 additions & 0 deletions boxcrete/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from boxcrete.models import (
FixedFeatureModel,
SustainableConcreteModel,
fit_gwp_gp,
fit_strength_gp,
get_strength_gp_input_transform,
)
from boxcrete.utils import (
DEFAULT_DATA_PATH,
DEFAULT_X_COLUMNS,
DEFAULT_Y_COLUMNS,
DEFAULT_YSTD_COLUMNS,
SustainableConcreteDataset,
get_day_zero_data,
get_mortar_bounds,
get_mortar_constraints,
get_reference_point,
load_concrete_strength,
)

__all__ = [
"FixedFeatureModel",
"SustainableConcreteModel",
"fit_gwp_gp",
"fit_strength_gp",
"get_strength_gp_input_transform",
"DEFAULT_DATA_PATH",
"DEFAULT_X_COLUMNS",
"DEFAULT_Y_COLUMNS",
"DEFAULT_YSTD_COLUMNS",
"SustainableConcreteDataset",
"get_day_zero_data",
"get_mortar_bounds",
"get_mortar_constraints",
"get_reference_point",
"load_concrete_strength",
]
Loading