Skip to content

Commit f50f4b5

Browse files
Unify mortar/concrete optimization, consolidate constraints, 100% test coverage
- Rename BOxCrete_data.csv → boxcrete_data.csv (lowercase, remove clay columns) - Remove compressive_strength.csv (subsumed by boxcrete data) - Rename strength_model_tutorial.ipynb → prediction_and_optimization_tutorial.ipynb - Update all constants to boxcrete column names (with units) - Consolidate get_mortar_bounds + get_bounds → single get_bounds with MORTAR_BOUNDS_DICT / CONCRETE_BOUNDS_DICT presets - Consolidate get_mortar_constraints + get_concrete_constraints → single get_constraints with equality_sums parameter and MORTAR_CONSTRAINTS / CONCRETE_CONSTRAINTS presets - Inline thin constraint wrapper functions into get_constraints - Generalize get_model_list(fixed_features=None) for mortar/concrete optimization — backward compatible, wraps GWP model when non-Time features are fixed - Add reduce_to_optimization_space for dimension reduction of bounds and constraints when using FixedFeatureModel - Add boxcrete/plotting.py with plot_strength_curve utility - Parameterize notebooks with optimization_mode env var (mortar/concrete) - Fix demo notebook hardcoded tensors → data-driven column lookup - CI executes notebooks in both mortar and concrete modes - Add docstrings to all public methods and classes - 79 tests, 100% coverage
1 parent e32deea commit f50f4b5

23 files changed

Lines changed: 2992 additions & 2242 deletions

.github/workflows/notebooks.yml

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,24 @@ jobs:
2929
strategy:
3030
fail-fast: false
3131
matrix:
32-
python-version: ['3.10']
32+
optimization-mode: ['mortar', 'concrete']
3333

3434
steps:
3535
- name: Checkout repository
3636
uses: actions/checkout@v4
3737

38-
- name: Set up Python ${{ matrix.python-version }}
38+
- name: Set up Python 3.10
3939
uses: actions/setup-python@v5
4040
with:
41-
python-version: ${{ matrix.python-version }}
41+
python-version: '3.10'
4242

4343
- name: Cache pip dependencies
4444
uses: actions/cache@v4
4545
with:
4646
path: ~/.cache/pip
47-
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
47+
key: ${{ runner.os }}-pip-3.10-${{ hashFiles('pyproject.toml') }}
4848
restore-keys: |
49-
${{ runner.os }}-pip-${{ matrix.python-version }}-
49+
${{ runner.os }}-pip-3.10-
5050
${{ runner.os }}-pip-
5151
5252
- name: Install dependencies
@@ -58,18 +58,21 @@ jobs:
5858
run: |
5959
python -m ipykernel install --user --name python3
6060
61-
- name: Execute notebooks
61+
- name: Execute notebooks (${{ matrix.optimization-mode }} mode)
62+
env:
63+
BOXCRETE_OPTIMIZATION_MODE: ${{ matrix.optimization-mode }}
6264
run: |
6365
python - << 'PYEOF'
64-
import subprocess, sys
66+
import subprocess, sys, os
6567
from pathlib import Path
6668
69+
mode = os.environ.get("BOXCRETE_OPTIMIZATION_MODE", "concrete")
6770
notebooks = sorted(Path("notebooks").glob("*.ipynb"))
6871
failed = []
6972
7073
for nb in notebooks:
7174
print(f"{'=' * 40}")
72-
print(f"Executing: {nb}")
75+
print(f"Executing ({mode}): {nb}")
7376
print(f"{'=' * 40}")
7477
7578
result = subprocess.run(
@@ -87,25 +90,25 @@ jobs:
8790
8891
if result.returncode != 0:
8992
failed.append(str(nb))
90-
print(f"❌ Failed: {nb}")
93+
print(f"❌ Failed ({mode}): {nb}")
9194
else:
92-
print(f"✅ Successfully executed: {nb}")
95+
print(f"✅ Successfully executed ({mode}): {nb}")
9396
print()
9497
9598
if failed:
96-
print(f"\n{len(failed)} notebook(s) failed:")
99+
print(f"\n{len(failed)} notebook(s) failed in {mode} mode:")
97100
for f in failed:
98101
print(f" - {f}")
99102
sys.exit(1)
100103
else:
101-
print(f"\nAll {len(notebooks)} notebook(s) executed successfully.")
104+
print(f"\nAll {len(notebooks)} notebook(s) executed successfully in {mode} mode.")
102105
PYEOF
103106
104107
- name: Upload executed notebooks as artifacts
105108
uses: actions/upload-artifact@v4
106109
if: always()
107110
with:
108-
name: executed-notebooks
111+
name: executed-notebooks-${{ matrix.optimization-mode }}
109112
path: notebooks/*.ipynb
110113
retention-days: 7
111114

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,17 @@ Thumbs.db
5656
*.tar.gz
5757
*.zip
5858

59+
# Legacy root-level modules (superseded by boxcrete/ package)
60+
/models.py
61+
/utils.py
62+
/input_transform.py
63+
/doodles.py
64+
/posterior_mean_pareto.py
65+
66+
# Notebook-generated output files
67+
ConcreteFormulae.csv
68+
ConcreteFormula_*.png
69+
GWPvsStrength.png
70+
5971
# Planning files
6072
*.plan.md

README.md

Lines changed: 107 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# BOxCrete: A Bayesian Optimization open-source AI Model for Concrete Mix Design & Optimization
22

3-
Concrete, the second most widely used material in the world, accounts for **6–8% of global anthropogenic CO₂ emissions**, largely due to Portland cement production (~0.8 tons CO₂ per ton of cement). Partial replacement with Supplementary Cementitious Materials (SCMs) such as fly ash, slag, and natural pozzolan reduces embodied carbon and often improves durability, but high SCM usage makes compressive strength a highly nonlinear function of multiple interacting mix parameters, rendering traditional design empirical and trial-and-error driven. To systematically navigate this complex composition space, data-driven frameworks are needed.
4-
Here, we introduce BOxCrete, an open-source Bayesian optimization framework for probabilistic strength curve prediction and sustainable mix design.
3+
Concrete, the second most widely used material in the world, accounts for **6–8% of global anthropogenic CO₂ emissions**, largely due to Portland cement production (~0.8 tons CO₂ per ton of cement). Partial replacement with Supplementary Cementitious Materials (SCMs) such as fly ash, slag, and natural pozzolan reduces embodied carbon and often improves durability, but high SCM usage makes compressive strength a highly nonlinear function of multiple interacting mix parameters, rendering traditional design empirical and trial-and-error driven. To systematically navigate this complex composition space, data-driven frameworks are needed.
4+
Here, we introduce BOxCrete, an open-source Bayesian optimization framework for probabilistic strength curve prediction and sustainable mix design.
55
We invite researchers and practitioners of both machine learning and civil engineering
66
to collaborate on discovering more sustainable concrete formulations that are applicable
77
to a wide array of construction projects, at scale.
@@ -13,9 +13,11 @@ This repository contains probabilistic models and data for the
1313
1) Compressive strength of concrete and mortar mixes
1414
2) The associated global warming potential (GWP)
1515

16-
as a function of their composition, consisting of
17-
cement, slag, water, to name a few basic ingredients.
18-
See `boxcrete/models.py` for implementation details.
16+
as a function of their composition, consisting of cement, fly ash, slag, fine and coarse aggregate, admixtures, and water, to name a few basic ingredients. See `boxcrete/models.py` for implementation details.
17+
18+
### Included Datasets
19+
20+
- **BOxCrete data** (`data/boxcrete_data.csv`): Combined mortar and concrete mix compositions with strength measurements at multiple curing ages, GWP values, and multiple material sources. This is the single unified dataset used for all model training.
1921

2022
## Installation
2123

@@ -44,54 +46,137 @@ pip install -e ".[notebooks]"
4446
## Usage
4547

4648
```python
49+
import torch
50+
from boxcrete.utils import load_concrete_strength, get_bounds
4751
from boxcrete.models import SustainableConcreteModel
48-
from boxcrete.utils import load_concrete_strength, get_mortar_bounds
52+
from boxcrete.plotting import plot_strength_curve
53+
54+
# Load data and fit models
55+
data = load_concrete_strength()
56+
data.bounds = get_bounds(data.X_columns)
57+
model = SustainableConcreteModel(strength_days=[1, 28])
58+
model.fit_gwp_model(data)
59+
model.fit_strength_model(data)
60+
61+
# model_list[0] = GWP, model_list[1] = 1-day strength, model_list[2] = 28-day strength
62+
model_list = model.get_model_list()
63+
64+
# Plot strength curves: 100% cement vs 60% fly ash + 40% cement
65+
cols = data.X_columns[:-1] # composition columns (without Time)
66+
compositions = torch.zeros(2, len(cols))
67+
compositions[0, cols.index("Cement (kg/m3)")] = 500.0 # 100% cement
68+
compositions[1, cols.index("Cement (kg/m3)")] = 200.0 # 40% cement
69+
compositions[1, cols.index("Fly Ash (kg/m3)")] = 300.0 # 60% fly ash
70+
plot_strength_curve(model, compositions)
4971
```
5072

5173
The models can be used for a variety of tasks, including but not limited to
52-
1) Continuous-time strength curve predictions with uncertainty bands for a user-specified concrete mix.
53-
2) Experimental design: suggesting promising concrete mixtures to be tested in a lab,
54-
3) The computation of optimal strength-GWP trade-offs based on user-specified (possibly location-specific) constraints.
55-
74+
1) Continuous-time strength curve predictions with uncertainty bands for a user-specified concrete mix.
75+
2) Experimental design: suggesting promising concrete mixtures to be tested in a lab,
76+
3) The computation of optimal strength-GWP trade-offs based on user-specified (possibly location-specific) constraints.
5677

5778
# Examples
5879

5980
## Compressive Strength Model
6081

61-
The `SustainableConcreteModel` in ['BOxCrete_models.py'](BOxCrete_models.py) includes a strength_model that predicts the evolution of compressive strength as a function of mixture composition. A tutorial is provided in [notebooks/BOxCrete Concrete Strength Prediction for GitHub.ipynb](<notebooks/BOxCrete Concrete Strength Prediction for GitHub.ipynb>), which demonstrates how the model can be used to predict the full strength development curve for any user-specified mix. The model is based on Gaussian Process (GP) regression and incorporates custom modeling steps to ensure physically consistent strength evolution and calibrated uncertainty. Example strength curve predictions generated using the notebook are shown in the figures below.
82+
The `SustainableConcreteModel` in [`boxcrete/models.py`](boxcrete/models.py) includes a strength_model that predicts the evolution of compressive strength as a function of mixture composition. A demo is provided in [`notebooks/strength_curve_prediction_demo.ipynb`](notebooks/strength_curve_prediction_demo.ipynb), which demonstrates how the model can be used to predict the full strength development curve for any user-specified mix. A comprehensive tutorial covering prediction, calibration, Pareto frontiers, and gradient-based experimental design is available in [`notebooks/prediction_and_optimization_tutorial.ipynb`](notebooks/prediction_and_optimization_tutorial.ipynb). The model is based on Gaussian Process (GP) regression and incorporates custom modeling steps to ensure physically consistent strength evolution and calibrated uncertainty.
83+
84+
### Strength Curve Predictions
85+
86+
The following figure shows predicted strength curves for two compositions: portland cement (blue) and a mix with high cement substitution (green). The model captures the distinct strength development trajectories associated with different binder chemistries while providing physically consistent uncertainty estimates.
6287

6388
<p align="center">
64-
<img src="fig/Picture1.png">
89+
<img src="fig/concrete_strength_curves.png">
6590
</p>
6691

67-
The figure shows predicted strength curves for two compositions: portland cement (blue) and a mix with high cement substitution (green). The model captures the distinct strength development trajectories associated with different binder chemistries while providing physically consistent uncertainty estimates.
92+
### Model Calibration
93+
94+
#### Cross-Validation on Independent Test Set
6895

69-
When the model is trained on the full training dataset and evaluated on an independent set of mixtures, it similarly demonstrates strong predictive performance. As shown in the figure below, the predicted compressive strengths closely match the experimentally measured values across the range of mixes and curing ages, indicating that the model successfully generalizes beyond the training data and provides reliable strength forecasts.
96+
When the model is trained on the full training dataset and evaluated on an independent set of mixtures, it demonstrates strong predictive performance. The predicted compressive strengths closely match the experimentally measured values across the range of mixes and curing ages.
7097

7198
<p align="center">
72-
<img src="fig/Picture2.png">
99+
<img src="fig/concrete_cross_validation.png">
73100
</p>
74101

75-
Further, when trained on the mortar and concrete mix strength data contained in this repository, the training set predictions also look sensible and well calibrated, as the next figure shows.
102+
#### Training Set Calibration
103+
104+
When trained on the mortar and concrete mix strength data contained in this repository, the training set predictions also look sensible and well calibrated.
76105

77106
<p align="center">
78-
<img src="fig/Picture3.png">
107+
<img src="fig/concrete_calibration.png">
79108
</p>
80109

81110
## Experimental Design
82111

83-
The probabilistic compressive strength model can also be used to design new concrete mixtures that achieve optimal trade-offs between mechanical performance and environmental impact. In particular, the framework enables multi-objective optimization of early-age (1-day) and later-age (28-day) compressive strength alongside Global Warming Potential (GWP). By systematically exploring the composition space, BOxCrete can generate candidate mixes that balance structural performance requirements with carbon reduction targets.
112+
### Inferring Optimal Trade-Offs under Constraints
113+
114+
While the previous section focused on using the models to predict strength curves,
115+
we can also use the trained model to predict what the optimal trade-offs between GWP and strength
116+
are likely to look like under constraints on the concrete composition
117+
that were not necessarily present during the training of the model.
118+
119+
In particular, the figure below shows the predicted Pareto frontiers
120+
of GWP and strength subject to two constraints on the water-to-binder ratio,
121+
i.e.:
122+
123+
1) water-to-binder ratio > 0.2 (solid lines), and
124+
2) water-to-binder ratio > 0.35 (dashed lines),
125+
126+
as well as constraints on ingredients:
127+
128+
1) no constraints (blue),
129+
2) no fly ash (orange), and
130+
3) no slag (green).
131+
132+
<p align="center">
133+
<img src="fig/predicted_pareto_frontiers.jpg">
134+
</p>
135+
136+
Notably, while the figure is purely based on model predictions,
137+
the trends in the figure conform to expert knowledge.
138+
In particular,
139+
- the increase in the minimum water-to-binder ratio has an outsize negative effect
140+
on the evolution of strength,
141+
- removing fly ash from the composition appears to have negligible effect during the time window we consider (< 28 days), and
142+
- removing slag from the composition has a signficiant negative effect on strength, similar to the increase in the water-to-binder ratio.
143+
144+
These are just a few insights we can gain from querying the model,
145+
and we believe that many more questions about the behavior of concrete
146+
can be investigated in a similar way.
147+
148+
From a practical perspective, the insight that the exclusion of slag - a by-product of steel production -
149+
is more signficiant than the exlusion of fly ash - a by-product of coal power plants -
150+
can inform site selection
151+
for large construction projects that seek to minimize carbon impact.
152+
153+
### Empirical Pareto Frontier Evolution
154+
155+
The probabilistic model for compressive strength can in addition be used to design new concrete mixtures that are likely to exhibit an optimal trade-off between strength and GWP.
156+
The following figure shows the evolution of the empirical Pareto frontier,
157+
i.e. the points with empirically optimal trade-offs,
158+
as a function of our experimental batches.
159+
160+
<p align="center">
161+
<img src="fig/empirical_pareto_frontiers.jpg">
162+
</p>
163+
164+
Importantly, the experimental design methodology has been able to propose mortar mixes
165+
that have experimentally proven to exhibit superior trade-offs between GWP and strength
166+
compared (orange-yellow) to human-designed mixes (blue-purple).
167+
168+
### Multi-Objective Optimization (Concrete Data)
84169

85-
As illustrated in the figure below, the model identifies a Pareto front capturing the trade-off between 1-day strength, 28-day strength, and GWP across candidate mixtures.
170+
The framework also enables multi-objective optimization of early-age (1-day) and later-age (28-day) compressive strength alongside Global Warming Potential (GWP). By systematically exploring the composition space, BOxCrete can generate candidate mixes that balance structural performance requirements with carbon reduction targets.
86171

87172
<p align="center">
88-
<img src="fig/Picture4.png">
173+
<img src="fig/concrete_pareto_front.png">
89174
</p>
90175

91-
Another figure shows the distribution of model-generated mixes plotted together with the training dataset, illustrating how the optimization explores the design space while remaining guided by experimentally validated compositions.
176+
The following figure shows the distribution of model-generated mixes plotted together with the training dataset, illustrating how the optimization explores the design space while remaining guided by experimentally validated compositions.
92177

93178
<p align="center">
94-
<img src="fig/Picture5.png">
179+
<img src="fig/concrete_optimization_design_space.png">
95180
</p>
96181

97182
# Citing

boxcrete/__init__.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,47 @@
1111
fit_strength_gp,
1212
get_strength_gp_input_transform,
1313
)
14+
from boxcrete.plotting import plot_strength_curve
1415
from boxcrete.utils import (
15-
DEFAULT_DATA_PATH,
16+
CONCRETE_BOUNDS_DICT,
17+
CONCRETE_CONSTRAINTS,
18+
DATA_PATH,
19+
DEFAULT_BOUNDS_DICT,
1620
DEFAULT_X_COLUMNS,
1721
DEFAULT_Y_COLUMNS,
1822
DEFAULT_YSTD_COLUMNS,
23+
MORTAR_BOUNDS_DICT,
24+
MORTAR_CONSTRAINTS,
1925
SustainableConcreteDataset,
26+
get_bounds,
27+
get_constraints,
2028
get_day_zero_data,
21-
get_mortar_bounds,
22-
get_mortar_constraints,
2329
get_reference_point,
2430
load_concrete_strength,
31+
reduce_to_optimization_space,
2532
)
2633

2734
__all__ = [
28-
"FixedFeatureModel",
29-
"SustainableConcreteModel",
30-
"fit_gwp_gp",
31-
"fit_strength_gp",
32-
"get_strength_gp_input_transform",
33-
"DEFAULT_DATA_PATH",
35+
"CONCRETE_BOUNDS_DICT",
36+
"CONCRETE_CONSTRAINTS",
37+
"DATA_PATH",
38+
"DEFAULT_BOUNDS_DICT",
3439
"DEFAULT_X_COLUMNS",
3540
"DEFAULT_Y_COLUMNS",
3641
"DEFAULT_YSTD_COLUMNS",
42+
"FixedFeatureModel",
43+
"MORTAR_BOUNDS_DICT",
44+
"MORTAR_CONSTRAINTS",
45+
"SustainableConcreteModel",
3746
"SustainableConcreteDataset",
47+
"fit_gwp_gp",
48+
"fit_strength_gp",
49+
"get_bounds",
50+
"get_constraints",
3851
"get_day_zero_data",
39-
"get_mortar_bounds",
40-
"get_mortar_constraints",
4152
"get_reference_point",
53+
"get_strength_gp_input_transform",
4254
"load_concrete_strength",
55+
"plot_strength_curve",
56+
"reduce_to_optimization_space",
4357
]

0 commit comments

Comments
 (0)