Skip to content
Merged
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
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,7 @@ Maintained and updated by Isaac S. Wheeler.
A web-based GUI is available for this software at http://lyopronto.geddes.rcac.purdue.edu.

# How to Use This Code Directly
Download this repository, then in your preferred command line navigate to the containing directory (so that `LyoPronto` is a subdirectory).
Execute:
```
python3 LyoPronto.main -m
```
This will execute the file `main.py` in an appropriate scope. Parameters can be changed in `main.py`. Files listing the inputs and outputs will be generated in the current directory, along with some plots of temperature, pressure, and drying progress vs. time.
A video tutorial by the authors illustrating this process can be found [on LyoHUB's YouTube channel](https://youtu.be/DI-Gz0pBI0w).

Alternatively, construct a YAML file with all the necessary inputs (see YAML files under `test_data` of this repository for examples), then run a Python script like the following:
Construct a YAML file with all the necessary inputs (see YAML files under `test_data` of this repository for examples), then run a Python script like the following:
Comment thread
Ickaser marked this conversation as resolved.
```python
import time

Expand All @@ -34,14 +26,23 @@ inputs = lp.read_inputs(yaml_fname)
output = lp.execute_simulation(inputs)

# Record the simulation inputs, outputs, and figures
save_inputs(inputs, current_time)
save_csv(output, inputs, current_time)
generate_visualizations(output, inputs, current_time)
lp.save_inputs(inputs, current_time)
lp.save_csv(output, inputs, current_time)
lp.generate_visualizations(output, inputs, current_time)
```
This will generate a record of both inputs and outputs each time you execute the file, so you can edit the original YAML and rerun the script without worrying about losing prior values of the inputs.

See also documentation examples online [here](https://lyohub.github.io/LyoPRONTO/dev/examples/knownRp_PD/) and [here](https://lyohub.github.io/LyoPRONTO/dev/examples/unknownRp_PD/)

The original method for running this code, as illustrated in a video tutorial
[on LyoHUB's YouTube channel](https://youtu.be/DI-Gz0pBI0w),
is to download this repository, edit the script `main.py` in its root directory, then run that script from the command line (in the same directory):
```
python main.py
```
Files listing the inputs and outputs will be generated in the current directory, along with some plots of temperature, pressure, and drying progress vs. time.


# Citation
G. Shivkumar, P. S. Kazarin, A. D. Strongrich, & A. A. Alexeenko, "LyoPRONTO: An Open-Source Lyophilization PRocess OptimizatioN TOol", AAPS PharmSciTech (2019) 20: 328.

Expand Down
6 changes: 3 additions & 3 deletions docs/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ Run
```
mike deploy [name]
```
to deploy a docs version with ID `[name]`, which could be e.g. `v1.1.0` or `pr-10`, etc. Preview locally by navigating to `LyoPRONTO_folder/site`, then running
to deploy a docs version with ID `[name]`, which could be e.g. `v1.1.0` or `pr-10`, etc. Preview locally by running
```
python -m http.server --bind localhost
mkdocs serve
```
to spin up a local HTTP server on your own machine.
which will spin up a local HTTP server showing the docs, which will hot-reload as you edit the files in `docs` or the configuration in `mkdocs.yml`

On pushing to master, GitHub actions will run
```
Expand Down
257 changes: 257 additions & 0 deletions docs/examples/freezing.ipynb

Large diffs are not rendered by default.

67 changes: 11 additions & 56 deletions docs/examples/knownRp_PD.ipynb

Large diffs are not rendered by default.

356 changes: 356 additions & 0 deletions docs/examples/opt_Pch_PD.ipynb

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions docs/how-to-guides.md
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
# How-to Guide

Under construction, but do note that the examples (e.g. for [known Rp](examples/knownRp_PD.ipynb) and [unknown Rp](examples/unknownRp_PD.ipynb) ) are generated from Jupyter notebooks, which you can find in the full repo and modify.
# How-to Guide
4 changes: 3 additions & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Full listing of all docstrings by module
# Reference

## Full listing of all docstrings by module

::: lyopronto
options:
Expand Down
33 changes: 31 additions & 2 deletions docs/tutorials.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# Tutorials

Under construction. But for now, see the video tutorial at https://www.youtube.com/watch?v=DI-Gz0pBI0w.
## Original approach

The short version here is, edit `main.py` in LyoPRONTO then execute it with `python -m LyoPRONTO.main`.
Edit `main.py` in the root folder of the repo, then execute it with `python main.py`.
See also the video tutorial at https://www.youtube.com/watch?v=DI-Gz0pBI0w.

## Newer approach: YAML Input Format

LyoPRONTO accepts inputs as a YAML file, loaded via `read_inputs(filename)` and executed with `execute_simulation(inputs)`.
The expected keys, values, etc. are fully specified with all units and constraints in the [full reference](reference.md#input-yaml-specification). The keys are also exemplified in the example notebooks, with a set of dictionaries that are put together into a `sim_setup` dictionary for each example.

With a YAML file for your inputs, the high-level workflow is as simple as this:

```python
import time

import lyopronto as lp

# get time for recording simulation results
current_time = time.strftime("%y%m%d_%H%M", time.localtime())

yaml_fname = "your_dir/cycle_setup.yaml" # Fill in with your filename and appropriate location

# Read in simulation inputs
inputs = lp.read_inputs(yaml_fname)
# Execute the simulation
output = lp.execute_simulation(inputs)

# Record the simulation inputs, outputs, and figures
lp.save_inputs(inputs, current_time)
lp.save_csv(output, inputs, current_time)
lp.generate_visualizations(output, inputs, current_time)
```
229 changes: 229 additions & 0 deletions docs/yaml_inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Input YAML Specification

LyoPRONTO reads simulation inputs from a YAML file via `read_inputs(filename)` and writes them back via `save_inputs(inputs, timestamp)`. The YAML uses `ruamel.yaml` (YAML 1.1, block style). Large arrays (`time_data`, `temp_data`) are stripped before writing; if your input references a temperature data file, the `product_temp_filename` key is preserved as a reminder, and you must load the data separately into `time_data` / `temp_data` before calling `execute_simulation()`.

When read into Python, this YAML becomes a dictionary of dictionaries: you can construct that dictionary-of-dictionaries structure yourself in a script and pass it to simulations.

## Top-Level Structure

```yaml
# always required:
sim: # simulation configuration (dict) — required
vial: # vial geometry (dict) — required
product: # product properties (dict) — required
ht: # heat transfer parameters (dict) — conditional
Pchamber: # chamber pressure profile (dict) — conditional shape
Tshelf: # shelf temperature profile (dict) — conditional shape
# sometimes required:
dt: 0.01 # time step (hr) — required
eq_cap: # equipment capability (dict) — design space / optimizer
nVial: 398 # number of vials — design space / optimizer
h_freezing: # freezing heat transfer coefficient — freezing only
t_dry_exp: # experimental drying time — Kv unknown only
Kv_range: # Kv search bounds — Kv unknown only
time_data: # experimental time array — Rp unknown only
temp_data: # experimental temperature array — Rp unknown only
product_temp_filename: # path to temperature file — Rp unknown only
```

---

## 1. `sim` — Simulation Configuration

| Key | Type | Description |
|---|---|---|
| `tool` | `str` | One of: `"Freezing Calculator"`, `"Primary Drying Calculator"`, `"Design Space Generator"`, `"Optimizer"` |
| `Kv_known` | `bool` | Whether the vial heat transfer coefficient is known (Drying calculator only)|
| `Rp_known` | `bool` | Whether product resistance is known (Drying calculator only)|
| `Variable_Pch` | `bool` | Chamber pressure is an optimization variable (Optimizer only) |
| `Variable_Tsh` | `bool` | Shelf temperature is an optimization variable (Optimizer only) |

**Constraints**:
- For `"Optimizer"`, at least one of `Variable_Pch` or `Variable_Tsh` must be `true`.
- For `"Primary Drying Calculator"`, `Kv_known` and `Rp_known` cannot both be `false`.

---

## 2. `vial` — Vial Geometry

| Key | Type | Unit | Description |
|---|---|---|---|
| `Av` | `float` | cm² | Vial cross-sectional area |
| `Ap` | `float` | cm² | Product surface area |
| `Vfill` | `float` | mL | Fill volume |

**Example**:
```yaml
vial:
Av: 3.8
Ap: 3.14
Vfill: 2.0
```

---

## 3. `product` — Product Properties

| Key | Type | Unit | Required When |
|---|---|---|---|
| `cSolid` | `float` | g/mL | Always |
| `Tpr0` | `float` | °C | `Freezing Calculator` |
| `Tf` | `float` | °C | `Freezing Calculator` |
| `Tn` | `float` | °C | `Freezing Calculator` |
| `R0` | `float` | cm²·hr·Torr/g | Always, unless `Rp_known` is `false` |
| `A1` | `float` | cm·hr·Torr/g | Always, unless `Rp_known` is `false` |
| `A2` | `float` | 1/cm | Always, unless `Rp_known` is `false` |
| `T_pr_crit` | `float` | °C | `Design Space Generator`, `Optimizer`, optional for `Primary Drying Calculator` |

**Notes**:

- `T_pr_crit` should be set 2–3 °C below the collapse or glass transition temperature, to allow a safety margin.
- For `"Primary Drying Calculator"` with `Rp_known == false`, only `cSolid` and `T_pr_crit` are needed.

**Examples**:

*Freezing*:
```yaml
product:
cSolid: 0.0
Tpr0: 15.8
Tf: -1.54
Tn: -5.84
```

*Drying (Rp known)*:
```yaml
product:
cSolid: 0.05
R0: 1.4
A1: 16.0
A2: 0.0
T_pr_crit: -5
```

---

## 4. `ht` — Heat Transfer Parameters

Required for all tools except `Freezing Calculator` or `Primary Drying Calculator` with `Kv_known == false`.
!!! info "Single value of Kv"
If you only have a single value of $K_v$ and you are simulating at the matching chamber pressure, you can put that value in as `KC` and set `KP`, `KD` both to zero.


| Key | Type | Unit | Description |
|---|---|---|---|
| `KC` | `float` | cal/(s·K·cm²) | Conductive/radiative coefficient |
| `KP` | `float` | cal/(s·K·cm²·Torr) | Pressure-dependent coefficient |
| `KD` | `float` | 1/Torr | Pressure-dependent denominator |

The effective vial coefficient is: `Kv = KC + KP × Pch / (1 + KD × Pch)`

**Example**:
```yaml
ht:
KC: 0.000275
KP: 0.000893
KD: 0.46
```

---

## 5. `Pchamber` — Chamber Pressure Profile

The structure depends on the simulation mode:

**Fixed setpoint** (Primary Drying Calculator, non-variable Optimizer):

| Key | Type | Unit | Description |
|---|---|---|---|
| `setpt` | `list[float]` | Torr | Pressure set points |
| `dt_setpt` | `list[float]` | min | Duration per set point (same length as `setpt`) |
| `ramp_rate` | `float` | Torr/min | Ramp rate between set points |

```yaml
Pchamber:
setpt: [0.15]
dt_setpt: [1800.0]
ramp_rate: 0.5
```

**Sweep array** (Design Space Generator):

```yaml
Pchamber:
setpt: [0.02, 0.05, 0.1, 0.15]
```

**Bounds** (Optimizer with `Variable_Pch == true`):

```yaml
Pchamber:
min: 0.05
max: 1000
```

Not required for `Freezing Calculator`.

---

## 6. `Tshelf` — Shelf Temperature Profile

**Fixed setpoint** (Primary Drying Calculator, non-variable Optimizer):

| Key | Type | Unit | Description |
|---|---|---|---|
| `init` | `float` | °C | Initial shelf temperature |
| `setpt` | `list[float]` | °C | Temperature set points |
| `dt_setpt` | `list[float]` | min | Duration per set point (same length as `setpt`) |
| `ramp_rate` | `float` | °C/min | Ramp rate between set points |

```yaml
Tshelf:
init: -35.0
setpt: [20.0]
dt_setpt: [1800.0]
ramp_rate: 1.0
```

**Sweep array** (Design Space Generator):

```yaml
Tshelf:
init: -5.0
setpt: [-15, 0, 30, 90]
ramp_rate: 1.0
```

**Bounds** (Optimizer with `Variable_Tsh == true`):

```yaml
Tshelf:
min: -45
max: 120
```

---

## 7. Equipment and Scalar Keys

| Key | Type | Unit | Required When | Description |
|---|---|---|---|---|
| `dt` | `float` | hr | Always | Simulation time step |
| `eq_cap.a` | `float` | kg/hr | Design Space & Optimizer | Equipment capability intercept |
| `eq_cap.b` | `float` | kg/hr/Torr | Design Space & Optimizer | Equipment capability slope |
| `nVial` | `int` | — | Design Space & Optimizer | Number of vials in batch |

Equipment capability models choked flow to the condenser: `dm/dt [kg/hr] = a + b × Pch [Torr]`

---

## 8. Other Top-level Keys by Mode

| Key | Type | Required When | Description |
|---|---|---|---|
| `h_freezing` | `float` (W/m²/K) | `Freezing Calculator` | Heat transfer coefficient during freezing |
| `t_dry_exp` | `float` (hr) | `Primary Drying Calculator` + `Kv_known == false` | Experimental drying time for Kv optimization |
| `Kv_range` | `list[float, float]` (cal/s/K/cm²) | `Primary Drying Calculator` + `Kv_known == false` | Lower and upper bounds for Kv root-finding |
| `time_data` | `list[float]` (hr) | `Rp_known == false` | Experimental time measurements |
| `temp_data` | `list[float]` (°C) | `Rp_known == false` | Experimental product temperature measurements |
| `product_temp_filename` | `str` | `Rp_known == false` | Path to temperature data file (informational only) |
10 changes: 5 additions & 5 deletions lyopronto/calc_knownRp.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ def dry(vial,product,ht,Pchamber,Tshelf,dt):
"""Simulate the primary drying process for known condiditions and parameters.

Args:
vial (dict): see master simulation inputs
product (dict): see master simulation inputs
ht (dict): see master simulation inputs
Pchamber (dict): see master simulation inputs
Tshelf (dict): see master simulation inputs
vial (dict): Vial properties, including 'Vfill', 'Ap', and 'Av'..
product (dict): Product properties, including 'cSolid' and Rp parameters 'R0', 'A1', and 'A2'.
ht (dict): Heat transfer properties, including 'KC', 'KP', and 'KD'.
Pchamber (dict): Chamber pressure set points and time (see docs).
Tshelf (dict): Shelf temperature set points and time (see docs).
dt (float): Fixed time step for output [hours]

Returns:
Expand Down
20 changes: 17 additions & 3 deletions lyopronto/calc_unknownRp.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,23 @@

################# Primary drying at fixed set points ###############

def dry(vial,product,ht,Pchamber,Tshelf,time,Tbot_exp):

################## Initialization ################
def dry(vial, product, ht, Pchamber, Tshelf, time, Tbot_exp):
"""Simulates the primary drying process for a vial.

Args:
vial (dict): Vial properties, including 'Vfill', 'Ap', and 'Av'..
product (dict): Product properties, including 'cSolid'.
ht (dict): Heat transfer properties, including 'KC', 'KP', and 'KD'.
Pchamber (dict): Chamber pressure set points and time (see docs).
Tshelf (dict): Shelf temperature set points and time (see docs).
time (np.ndarray): Array of time points for the simulation.
Tbot_exp (np.ndarray): Array of expected bottom temperatures at each time step.

Returns:
tuple[np.ndarray, np.ndarray]: A tuple containing:
output_saved (np.ndarray): Saved output data for each time step, including time, sublimation temperature, bottom temperature, shelf temperature, chamber pressure, sublimation rate, and percent dried.
product_res (np.ndarray): Calculated product resistance data, including time, cake length, and product resistance.
"""

# Initial fill height
Lpr0 = functions.Lpr0_FUN(vial['Vfill'],vial['Ap'],product['cSolid']) # [cm]
Expand Down
Loading
Loading