Skip to content

Latest commit

Β 

History

History
393 lines (252 loc) Β· 16.1 KB

README.md

File metadata and controls

393 lines (252 loc) Β· 16.1 KB

SFPPy - Python Framework for Food Contact Compliance and Risk Assessment 🍏⏩🍎

πŸ› οΈ Overview

SFPPy is a Python-based framework for compliance testing of food contact materials and recycled plastic safety assessment under:

  • πŸ‡ΊπŸ‡Έ US FDA regulations
  • πŸ‡ͺπŸ‡Ί European Union regulations (EFSA, EU 10/2011, etc.)
  • πŸ‡¨πŸ‡³ Chinese GB standards
  • 🌍 Other international guidelines

This project translates well-established chemical migration models from MATLAB and other languages to pure Python, ensuring minimal dependencies.

πŸ“ Main Modules (Located in patankar/)

  • migration.py πŸ—οΈ - Core solver using a Patankar finite-volume method for mass transfer modeling.
  • geometry.py πŸ“ - Defines 3D packaging geometries and calculates volume/surface area.
  • food.py 🍎 - Models food layers and their interactions with packaging.
  • layer.py πŸ“œ - Defines materials and layers for multilayer packaging.
  • property.py πŸ“Š - Computes physical and chemical properties (e.g., diffusion, partitioning).
  • loadpubchem.py πŸ”¬ - Retrieves molecular properties from PubChem (cached locally).

Why Patankar?

πŸ“œ Click to expand

πŸ’‘ The patankar folder is named in honor of Suhas V. Patankar, who developed and popularized the finite volume method, which this project adapts for mass transfer problems with an arbitrary number of Rankine discontinuities.

πŸ”§ The modules include a knowledge management system via extensible classes, allowing easy expansion to cover additional cases and implement new prediction methods.


πŸš€ Quick Start

# Clone the repository
git clone https://github.com/ovitrac/SFPPy.git
cd SFPPy

# Install dependencies
pip install -r requirements.txt

🧭 Learn how to use SFPPy from Wiki Pages.



πŸ’‘ Usage Snippets

SFPPy is fully object-oriented and supports multiple syntax styles, ranging from a functional approach to a more abstract, operator-driven paradigmβ€”all in a Pythonic manner. The snippets below demonstrate both approaches.

Snippet 1️⃣ | Simple Migration Simulation

πŸ“œ Click to expand
from patankar.food import ethanol  # food database
from patankar.layer import layer  # material database

# Define the food contact medium and layers
simulant = ethanol() # here a food simulant
A = layer(layername="layer 1 (contact)", D=1e-15, l=50e-6, C0=0, k=1)  # SI units
B = layer(layername="layer 2", D=(1e-9, "cm**2/s"), l=(100, "um"),k=2)
multilayer = A + B  # layer A is contact (food is on the left)

# Run solver, plot the migration kinetics CF(t) and concentration profiles in P Cx(x,t)
solution = simulant.migration(multilayer)
hCF = solution.plotCF()  # concentration kinetic in the simulant (F) for default times
hCx = solution.plotCx()  # concentration profile in the multilayer packaging
# Print in PDF and PNG, export to Excel
hCF.print("myresult")
solution.comparison.save_as_csv("myresult.csv") # CSV format
solution.comparison.save_as_excel("myresult.xlsx") # Excel format

πŸ“ Notations: $D$ is the diffusivity, $l$ is the thickness layer, and $C_0$ is the initial concentration.

CFCx


Snippet 2️⃣ | Retrieving Molecular Properties and Toxicological Data

πŸ” Click to expand
from patankar.loadpubchem import migrant  # connect to pubchem for missing substances
from patankar.food import oliveoil,water  # food simulants 
from patankar.layer import gPET           # "glassy" PET (i.e., T<Tg)
m = migrant("bisphenol A")                # bisphenol A = BPA
# Print basic properties
print(m.M, m.logP, m.polarityindex)       # Molecular weight, logP value, polarity Index P'
print(m.smiles)                           # CC(C)(C1=CC=C(C=C1)O)C2=CC=C(C=C2)O

# Add BPA to material (P) and food simulants (F1,F2) to calculate binary properties
F1 = oliveoil(migrant=m)                  # F1 = food simulant oliver oil with BPA
F2 = water(migrant=m)                     # F2 = water with BPA
P = gPET(migrant=m)                       # P = PET with BPA
KFP1 = P.k / F1.k     # F-to-P1 partition coefficient, k= Henry-like coefficients
KFP2 = P.k / F2.k     # F-to-P2 partition coefficient, k= Henry-like coefficients
# Print partition coefficients, with k values calculated from Flory-Huggins theory
print(KFP1,KFP2)      # [0.93498524] [0.00093499]

πŸ’‘ The examples show how to inject m intoΒ  $F$=food (various classes )Β and $P$=polymer layer (various classes) to get customized and conservative simulations for specific substances and polymers. All properties, diffusivities $D$, Henry-like coefficients $k$Β are calculated automatically based from their names.

Add toxicological data from Toxtree

from patankar.loadpubchem import migrantToxtree # combine PubChem and ToxTree
substance = migrantToxtree("formaldehyde")

output

<migrantToxtree object>
     Compound: formaldehyde
         Name: formaldehyde
          cid: 712
          CAS: ['50-00-0', '8013-13 [...] 80-5', '30525-89-4']
      M (min): 30.026
      M_array: [30.026]
      formula: CH2O
       smiles: C=O
         logP: [1.2]
    P' (calc): [3.91591487]
   Toxicology: Low (Class I)
          TTC: 1.5 [Β΅g/kg bw/day]
       CF TTC: 0.09 [mg/kg food intake]
      alert 1: Alert For Schiff Bas [...] Formation Identified
Out: <migrantToxtree: Oplossin [...]  [Dutch] - M=30.026 g/mol>

πŸ’‘ A local installation of Toxtree (java) is included with SFPPy

For the European FCM and Articles Regulation, Annex I - Authorised Substances, use the ECHA webpage


Snippet 3️⃣ | Defining a Custom Packaging Shape

πŸ“¦ Click to expand
from patankar.geometry import Packaging3D  # import basic shapes
pkg = Packaging3D('bottle', # bottle is a composite shape
                  body_radius=(5, 'cm'), body_height=(0.2, 'm'),
                  neck_radius=(19, "mm"), neck_height=(40, "mm"))
vol, area = pkg.get_volume_and_area() # extract volume and surface area
print("Volume (mΒ³):", vol)
print("Surface Area (mΒ²):", area)

πŸ’‘ The examples show how to use either pkg or its properties to achieve mass transfer simulation for a specific geometry.

⚠️ Note: To efficiently simulate the migration of substances from packaging materials, SFPPy unfolds complex 3D packaging geometries into an equivalent 1D representation. This transformation assumes that substance desorption is predominantly governed by diffusion within the walls of the packaging.

πŸ” The geometry.py module provides tools to compute surface-area-to-volume ratios, extract wall thicknesses, and generate equivalent 1D models for mass transfer simulations.


Snippet 4️⃣ | Using ⏩ as Mass Transfer Operator in Chained Simulations

πŸ“¦ Click to expand

πŸ“Œ SFPPy leverages multiple inheritance to define food contact conditions by combining storage conditions, food types, and physical properties.

πŸ“Œ Additionally, two operators play a key role in SFPPy’s intuitive syntax:

  • βž• for combining layers and merging results
  • ⏩ for naturally representing mass transfer

With these operators, mass transfer can be abstracted into a simple, visual representation:

  1. 🍏⏩🍎
    (Direct transfer from green to red, symbolizing migration.)

  2. 🍏⏩🟠⏩🍎
    (Includes an intermediate step, depicting progressive migration.)

  3. 🍏⏩🟑⏩🟠⏩🍎
    (More detailed, illustrating multiple contamination stages over time.)

  4. 🍏⚑⏩🍎
    (Emphasizes active food transformation, with accelerated mass transfer.)

🌟 SFPPy makes this abstraction possible with simple, expressive code.

from patankar.layer import gPET, PP
from patankar.food import ambient, hotfilled, realfood, fat, liquid, stacked
from patankar.loadpubchem import migrant

# Define migrant and packaging layers (ABA: PET-PP-PET)
m = migrant("limonene")
A = gPET(l=(20, "um"), migrant=m, C0=0)
B = PP(l=(500, "um"), migrant=m, C0=200)  
ABA = A + B + A  # the most left layer is contact (food on the left)

# Define storage and processing conditions:
# 1:storage in stacks >> 2:hot-filled container >> 3:long-term storage of packaged food
class contact1(stacked, ambient): name = "1:setoff"; contacttime = (4, "months")
class contact2(hotfilled, realfood, liquid, fat): name = "2:hotfilling"
class contact3(ambient, realfood, liquid, fat): name = "3:storage"; contacttime = (6, "months")

# Instantiate and simulate with ⏩
medium1, medium2, medium3 = contact1(), contact2(), contact3()
medium1 >> ABA >> medium1 >> medium2 >> medium3  # Automatic chaining

# Merge all kinetics into a single one and plot the migration kinetics
sol123 = medium1.lastsimulation + medium2.lastsimulation + medium3.lastsimulation
sol123.plotCF()

CF

🧩 How It Works

Each contact class inherits attributes from multiple base classes, allowing flexible combinations of:

  1. πŸ“Œ Storage Conditions:

    • ambient: Defines standard storage at room temperature
    • hotfilled: Represents high-temperature filling processes
    • stacked: Models setoff migration when packaging layers are stacked
  2. πŸ₯˜ Food Types & Interactions:

    • realfood: Represents actual food matrices
    • liquid: Specifies that the food is a liquid
    • fat: Indicates a fatty food, influencing partitioning behavior

πŸ”¬ By combining these components, SFPPy allows streamlined, physics-based simulations with minimal code. πŸš€


Snippet 5️⃣ | Parameter linking πŸ”— via layerLink

πŸ“¦ Click to expand
# Any numeric property can be attached to a simulation with layerLink
from patankar.layer import layerLink
# Attach a variable function barrier thickness to ABA
fb_thickness = layerLink("l",indices=0) # index 0 = layer 1 (A) in contact with F
# Reuse ABA from Snippet 3 [...]
ABA.llink = fb_thicknesses
# Change dynamically the simulation by changing fb_thicknesses[0]
fb_thicknesses[0] = 12e-6 # 12 Β΅m
medium1.lastsimulation.rerun()
# [...]

πŸ’‘ Dynamic parameter binding using layerLink connections allows: βœ… Dynamic updates of $D$, $k$, $l$, $C_0$ abd $T$ for specific layers only ( index [i] refers to the layer i+1). βœ… Seamless integration of simulation and optimization tasks. βœ… Robust handling of parameter uncertainties in complex simulation scenarios.



πŸ“– Case Studies

The project includes four detailed examples (example1.py, example2.py, example3.py, and **example4.py**), showcasing real-world scenarios with various materials, substances, food types, geometries, and usage conditions.

Example 1: | Mass Transfer from β™Ά Monolayer Materials

  • πŸ₯ͺ Simulates the migration of Irganox 1076 and Irgafos 168 from a 100 Β΅m LDPEfilm into a fatty sandwich πŸ₯–over 10 days at 7Β°C.
  • πŸ“ˆ Evaluates migration kinetics and their implications for food safety.

Example 2| Mass Transfer in ♻️ Recycled PP Bottles

  • 🍼 Investigates toluenekbd< migration from a 300 Β΅m thick recycled PP bottle into a fatty liquid food.
  • πŸ›‘οΈ Assesses the effect of a PET functional barrier (FB) of varying thickness on reducing migration.

Example 3 | Advanced Migration Simulation ⛓️ with Variants

  • πŸ“¦ Simulates migration in a trilayer (ABA) multilayer system, with PET (A) and recycled PP (B).
  • πŸ”₯ Evaluates migration behavior across storage with set-off, hot-filling, and long-term storage conditions.
  • βš™οΈ Explores variants where the migrant and layer thickness are modified to assess performance.
  • 🍏⏩🍎 Example 3 showcases the mass transfer operator ⏩.

Example 4 | Parameter Fitting and Optimization βš™οΈ

  • βœ… Fit diffusivities ($D$) and partitioning coefficients ($\frac{k}{k_0}$) from migration kinetic data πŸ“ˆ.
  • βœ… Utilize dynamic parameter linking πŸ”—πŸ§² with layerLink.
  • βœ… Integrate simulation results directly with experiments for sensitivity analysis and optimization

⚠️ Disclaimer: These examples do not discuss sources of uncertainty. Please refer to our publications for details on the limitations of the presented approaches and assumptions.



🌟 Why SFPPy?

βœ” SFPPy: is free and opensource. βœ” SFPPy: accepts any unit as (value,"unit") or ([value1,value2...],"unit"). βœ” Operator-based chaining: >> handles automatic mass transfer and property propagation βœ” Minimal code for complex simulations: + joins layers and merges results across storage conditions βœ” Pythonic abstraction: Works with PubChem, ToxTree, predefined polymer materials, and 3D packaging geometries βœ” Built-in visualization & export: Supports Excel (.xlsx), CSV, PDF, PNG and Matlab (if its really needed)

πŸ”¬ SFPPy powers scalable, real-world safe food packaging simulations.


πŸ“œ License

MIT License


🀝 Contributors

INRAE - Olivier Vitrac
This project is part of the SFPPy initiative, aiming to bring the SafeFoodPackaging Portal version 3 (SFPP3) to the general public.

$2025-02-12$


For further details, consult the online documentation and the release page for new capabilities.


🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️
🍽️🍽️🍎🍎🍎🍎🍽️🍽️🍎🍎🍎🍎🍎🍽️🍽️🍏🍏🍏🍏🍽️🍽️🍽️🍎🍎🍎🍎🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️
🍽️🍎🍽️🍽️🍽️🍽️🍽️🍽️🍎🍽️🍽️🍽️🍽️🍽️🍽️🍏🍽️🍽️🍽️🍏🍽️🍽️🍎🍽️🍽️🍽️🍎🍽️🍽️🐍🍽️🍽️🍽️🐍🍽️
🍽️🍽️🍎🍎🍎🍽️🍽️🍽️🍎🍎🍎🍎🍽️🍽️🍽️🍏🍏🍏🍏🍽️🍽️🍽️🍎🍎🍎🍎🍽️🍽️🍽️🍽️🐍🍽️🐍🍽️🍽️
🍽️🍽️🍽️🍽️🍽️🍎🍽️🍽️🍎🍽️🍽️🍽️🍽️🍽️🍽️🍏🍽️🍽️🍽️🍽️🍽️🍽️🍎🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🐍🍽️🍽️🍽️
🍽️🍎🍎🍎🍎🍽️🍽️🍽️🍎🍽️🍽️🍽️🍽️🍽️🍽️🍏🍽️🍽️🍽️🍽️🍽️🍽️🍎🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🐍🍽️🍽️🍽️
🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️🍽️ $v1.30$

Enlarge your window if you cannot read the logo. The snake is the totem for Python