diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..465d08741781599c998aa94a12eede9fbc4ea9f1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.gitignore +.gitattributes +.env +__pycache__/ +*.pyc +*.pyo +*.pyd +.venv/ +.hf_vendor/ +outputs/ +Showcase.gif diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..44702936f4edb13a03c4a04989a86d94ba0c7d91 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,35 +1,19 @@ -*.7z filter=lfs diff=lfs merge=lfs -text -*.arrow filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.bz2 filter=lfs diff=lfs merge=lfs -text -*.ckpt filter=lfs diff=lfs merge=lfs -text -*.ftz filter=lfs diff=lfs merge=lfs -text -*.gz filter=lfs diff=lfs merge=lfs -text -*.h5 filter=lfs diff=lfs merge=lfs -text -*.joblib filter=lfs diff=lfs merge=lfs -text -*.lfs.* filter=lfs diff=lfs merge=lfs -text -*.mlmodel filter=lfs diff=lfs merge=lfs -text -*.model filter=lfs diff=lfs merge=lfs -text -*.msgpack filter=lfs diff=lfs merge=lfs -text -*.npy filter=lfs diff=lfs merge=lfs -text -*.npz filter=lfs diff=lfs merge=lfs -text -*.onnx filter=lfs diff=lfs merge=lfs -text -*.ot filter=lfs diff=lfs merge=lfs -text -*.parquet filter=lfs diff=lfs merge=lfs -text -*.pb filter=lfs diff=lfs merge=lfs -text -*.pickle filter=lfs diff=lfs merge=lfs -text -*.pkl filter=lfs diff=lfs merge=lfs -text -*.pt filter=lfs diff=lfs merge=lfs -text -*.pth filter=lfs diff=lfs merge=lfs -text -*.rar filter=lfs diff=lfs merge=lfs -text -*.safetensors filter=lfs diff=lfs merge=lfs -text -saved_model/**/* filter=lfs diff=lfs merge=lfs -text -*.tar.* filter=lfs diff=lfs merge=lfs -text -*.tar filter=lfs diff=lfs merge=lfs -text -*.tflite filter=lfs diff=lfs merge=lfs -text -*.tgz filter=lfs diff=lfs merge=lfs -text -*.wasm filter=lfs diff=lfs merge=lfs -text -*.xz filter=lfs diff=lfs merge=lfs -text -*.zip filter=lfs diff=lfs merge=lfs -text -*.zst filter=lfs diff=lfs merge=lfs -text -*tfevents* filter=lfs diff=lfs merge=lfs -text +# Auto detect text files and perform LF normalization +* text=auto +images/AISI[[:space:]]1045[[:space:]]and[[:space:]]AA5052_Ram[[:space:]]Speed.png filter=lfs diff=lfs merge=lfs -text +images/ASAAAA_Coefficient[[:space:]]'a'[[:space:]]for[[:space:]]average[[:space:]]equivalent[[:space:]]strain[[:space:]](ε̄_ave).png filter=lfs diff=lfs merge=lfs -text +images/ASAAAA_Flow[[:space:]]Stress[[:space:]]Equation[[:space:]](Lubricated).png filter=lfs diff=lfs merge=lfs -text +images/ASAAAA_Flow[[:space:]]Stress[[:space:]]Equation[[:space:]](Power's[[:space:]]Law).png filter=lfs diff=lfs merge=lfs -text +images/Epoxy[[:space:]]+[[:space:]]44%[[:space:]]Carbon[[:space:]]fiber_Tensile[[:space:]]Strength.png filter=lfs diff=lfs merge=lfs -text +images/Home.png filter=lfs diff=lfs merge=lfs -text +images/iPP_Curve[[:space:]]Error[[:space:]](Maximum).png filter=lfs diff=lfs merge=lfs -text +images/iPP_Injection[[:space:]]Pressure[[:space:]](Pinject)[[:space:]]Range.png filter=lfs diff=lfs merge=lfs -text +images/iPP_Injection[[:space:]]Rate[[:space:]](Rinject)[[:space:]]Range.png filter=lfs diff=lfs merge=lfs -text +images/iPP_Melting[[:space:]]temperature.png filter=lfs diff=lfs merge=lfs -text +images/iPP_Normalized[[:space:]]Root[[:space:]]Mean[[:space:]]Square[[:space:]]Error[[:space:]](NRMSE)[[:space:]]Distribution[[:space:]]Center.png filter=lfs diff=lfs merge=lfs -text +images/logo.png filter=lfs diff=lfs merge=lfs -text +images/Materials_bg_InDeS.png filter=lfs diff=lfs merge=lfs -text +images/us_deptenergy.jpg filter=lfs diff=lfs merge=lfs -text +logo.png filter=lfs diff=lfs merge=lfs -text +page_files/categorized/ESS-min.jpg filter=lfs diff=lfs merge=lfs -text +Showcase.gif filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d9005f2cc7fc4e65f14ed5518276007c08cf2fd0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,152 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/.streamlit/config.toml b/.streamlit/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..f79d9e853ec55b7b27e8d9638d8132b3c27815c6 --- /dev/null +++ b/.streamlit/config.toml @@ -0,0 +1,14 @@ +[theme] +base = "light" +primaryColor = "#1d4ed8" +backgroundColor = "#ffffff" +secondaryBackgroundColor = "#ffffff" +textColor = "#111827" +font = "sans serif" +borderColor = "rgba(0, 0, 0, 0)" +showWidgetBorder = false + +[theme.sidebar] +backgroundColor = "#ffffff" +secondaryBackgroundColor = "#ffffff" +textColor = "#111827" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b924b926fe6e00fbc2f889985935226a245ec008 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + STREAMLIT_BROWSER_GATHER_USAGE_STATS=false + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libglib2.0-0 \ + libgl1 \ + libsm6 \ + libxext6 \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt /app/requirements.txt +RUN pip install --upgrade pip && pip install -r /app/requirements.txt + +COPY . /app + +EXPOSE 7860 + +CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0", "--server.port=7860"] diff --git a/README.md b/README.md index be18fb490172585871f263d9b71d3af92baca831..c2f077f1d2ec1ee29aeba5af2c2006a50b90c47b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,29 @@ --- title: Mat Database -emoji: 📚 -colorFrom: purple -colorTo: yellow +emoji: "🧪" +colorFrom: blue +colorTo: indigo sdk: docker +app_port: 7860 pinned: false --- -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +# Mat Database + +Streamlit application for browsing and uploading material properties data. + +## Local Run + +```bash +pip install -r requirements.txt +streamlit run app.py +``` + +## Environment Variables + +- `GEMINI_API_KEY` (optional but required for PDF extraction with Gemini) +- `DB_HOST` (optional, required for database-backed browsing/upload) +- `DB_PORT` (optional, defaults to `5432`) +- `DB_NAME` (optional, required for database-backed browsing/upload) +- `DB_USER` (optional, required for database-backed browsing/upload) +- `DB_PASSWORD` (optional, required for database-backed browsing/upload) diff --git a/Showcase.gif b/Showcase.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef870f56ca3515b977e2d358d74d69f4df999841 --- /dev/null +++ b/Showcase.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27c97f8c4d109ef123d59a33fdd55c5893237e3ddef17714158d5e9c36869d25 +size 250617 diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..8c4cf39f94786342644a0b02d07c32b4bcf0cddf --- /dev/null +++ b/app.py @@ -0,0 +1,15 @@ +import streamlit as st + +st.set_page_config(layout="wide", initial_sidebar_state="collapsed") + +pages = { + "": [ + st.Page("page_files/Home.py", title="Home"), + st.Page("page_files/Categorized_Search.py", title="Categorized Search"), + st.Page("page_files/Upload_Data.py", title="Upload Data"), + st.Page("page_files/Contact_Team.py", title="Contact Team"), + ] +} + +pg = st.navigation(pages, position="top") +pg.run() \ No newline at end of file diff --git a/data_loader.py b/data_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..c826dc8e5102037044916c15761e270b7518d5da --- /dev/null +++ b/data_loader.py @@ -0,0 +1,116 @@ +from db import execute_query, fetch_all +import pandas as pd + +EMPTY_MATERIAL_COLUMNS = [ + "material_name", + "material_abbreviation", + "section", + "property_name", + "value", + "unit", + "english", + "test_condition", + "comments", +] + +def load_material_data(material_type: str) -> pd.DataFrame: + table_map = { + "Polymers": "Polymers", + "Fibers": "Fibers", + "Composites": "Composites_materials", + } + + table = table_map.get(material_type) + if not table: + return pd.DataFrame(columns=EMPTY_MATERIAL_COLUMNS) + + query = f""" + SELECT + material_name, + material_abbreviation, + section, + property_name, + value, + unit, + english, + test_condition, + comments + FROM "{table}" + """ + + try: + rows = fetch_all(query) + except Exception: + return pd.DataFrame(columns=EMPTY_MATERIAL_COLUMNS) + return pd.DataFrame(rows, columns=EMPTY_MATERIAL_COLUMNS) + +def get_all_sections(): + all_data = pd.concat([ + load_material_data("Polymers"), + load_material_data("Fibers"), + load_material_data("Composites"), + ], ignore_index=True) + + if all_data.empty or "section" not in all_data.columns: + return [] + return sorted(all_data["section"].dropna().unique().tolist()) + + +def insert_material_rows(df: pd.DataFrame) -> int: + if df is None or df.empty: + return 0 + + table_map = { + "Polymer": "Polymers", + "Fiber": "Fibers", + "Composite": "Composites_materials", + } + + insert_template = """ + INSERT INTO "{table}" ( + material_name, + material_abbreviation, + section, + property_name, + value, + unit, + english, + test_condition, + comments + ) VALUES ( + :material_name, + :material_abbreviation, + :section, + :property_name, + :value, + :unit, + :english, + :test_condition, + :comments + ) + """ + + inserted = 0 + for _, row in df.iterrows(): + table = table_map.get(row.get("material_class")) + if not table: + continue + + params = { + "material_name": row.get("material_name", ""), + "material_abbreviation": row.get("material_abbreviation", ""), + "section": row.get("section", ""), + "property_name": row.get("property_name", ""), + "value": row.get("value", ""), + "unit": row.get("unit", ""), + "english": row.get("english", ""), + "test_condition": row.get("test_condition", ""), + "comments": row.get("comments", ""), + } + + try: + inserted += execute_query(insert_template.format(table=table), params) + except Exception: + return inserted + + return inserted diff --git a/db.py b/db.py new file mode 100644 index 0000000000000000000000000000000000000000..a4fc9b72e75b0c9ea3cc5e44feaba7d0b7ceba03 --- /dev/null +++ b/db.py @@ -0,0 +1,48 @@ +# db.py +import os +from sqlalchemy import create_engine, text +from sqlalchemy.orm import sessionmaker +from dotenv import load_dotenv + +load_dotenv() + +DB_HOST = os.getenv("DB_HOST") +DB_PORT = os.getenv("DB_PORT", "5432") +DB_NAME = os.getenv("DB_NAME") +DB_USER = os.getenv("DB_USER") +DB_PASSWORD = os.getenv("DB_PASSWORD") + +DATABASE_URL = None +engine = None +SessionLocal = None + +if all([DB_HOST, DB_NAME, DB_USER, DB_PASSWORD]): + DATABASE_URL = f"postgresql+psycopg://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}" + engine = create_engine(DATABASE_URL, pool_pre_ping=True) + SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False) + + +def _require_engine(): + if engine is None: + raise RuntimeError( + "Database is not configured. Set DB_HOST, DB_PORT, DB_NAME, DB_USER, and DB_PASSWORD." + ) + +def fetch_all(query, params=None): + _require_engine() + with engine.connect() as conn: + result = conn.execute(text(query), params or {}) + return [dict(row._mapping) for row in result] + +def fetch_one(query, params=None): + _require_engine() + with engine.connect() as conn: + result = conn.execute(text(query), params or {}) + row = result.fetchone() + return dict(row._mapping) if row else None + +def execute_query(query, params=None): + _require_engine() + with engine.begin() as conn: + result = conn.execute(text(query), params or {}) + return result.rowcount diff --git a/images/AISI 1045 and AA5052_Ram Speed.png b/images/AISI 1045 and AA5052_Ram Speed.png new file mode 100644 index 0000000000000000000000000000000000000000..d3407a0e8f2a7fd54baeeac353143d46ea197842 --- /dev/null +++ b/images/AISI 1045 and AA5052_Ram Speed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0329479746a3ba20be80ab2a6324528c6e3cf1e0685eae4b24661215c26b687b +size 153384 diff --git a/images/AISI 1045 and AA5052_Strain Hardening Exponent (n).png b/images/AISI 1045 and AA5052_Strain Hardening Exponent (n).png new file mode 100644 index 0000000000000000000000000000000000000000..b012e01fb0b12862ce1a2733d4a09f6c2bab27ce Binary files /dev/null and b/images/AISI 1045 and AA5052_Strain Hardening Exponent (n).png differ diff --git a/images/AISI 1045 and AA5052_Strength Coefficient (K).png b/images/AISI 1045 and AA5052_Strength Coefficient (K).png new file mode 100644 index 0000000000000000000000000000000000000000..61c0f27009d1cacc5f6c78dca8b16024afa49bed Binary files /dev/null and b/images/AISI 1045 and AA5052_Strength Coefficient (K).png differ diff --git "a/images/ASAAAA_Coefficient 'a' for average equivalent strain (\303\216\302\265\303\214\342\200\236_ave).png" "b/images/ASAAAA_Coefficient 'a' for average equivalent strain (\303\216\302\265\303\214\342\200\236_ave).png" new file mode 100644 index 0000000000000000000000000000000000000000..35c053948c5c7e05ce49beb33cee2460f5b5acfb --- /dev/null +++ "b/images/ASAAAA_Coefficient 'a' for average equivalent strain (\303\216\302\265\303\214\342\200\236_ave).png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df86d791caec82de3f10f8c27dfb2cb34b18e97cd4ffd873e6afa8a13381f574 +size 107961 diff --git a/images/ASAAAA_Diameter.png b/images/ASAAAA_Diameter.png new file mode 100644 index 0000000000000000000000000000000000000000..272ad3a01579992fe1c4878e7639d3698e8832d7 Binary files /dev/null and b/images/ASAAAA_Diameter.png differ diff --git a/images/ASAAAA_Flow Stress Equation (Lubricated).png b/images/ASAAAA_Flow Stress Equation (Lubricated).png new file mode 100644 index 0000000000000000000000000000000000000000..0e710f392dbd720aeba9b9254506e3dea2af15fe --- /dev/null +++ b/images/ASAAAA_Flow Stress Equation (Lubricated).png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8d6abcb19f71eb2348c53d83fa84cbb46ccf3f505f2a676a358533eaa7d6f79 +size 828252 diff --git a/images/ASAAAA_Flow Stress Equation (Power's Law).png b/images/ASAAAA_Flow Stress Equation (Power's Law).png new file mode 100644 index 0000000000000000000000000000000000000000..0e710f392dbd720aeba9b9254506e3dea2af15fe --- /dev/null +++ b/images/ASAAAA_Flow Stress Equation (Power's Law).png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8d6abcb19f71eb2348c53d83fa84cbb46ccf3f505f2a676a358533eaa7d6f79 +size 828252 diff --git a/images/ASAAAA_Flow Stress Equation (Sticking).png b/images/ASAAAA_Flow Stress Equation (Sticking).png new file mode 100644 index 0000000000000000000000000000000000000000..ea41b234c64afa74b60da77df61b428fb7afc7c6 Binary files /dev/null and b/images/ASAAAA_Flow Stress Equation (Sticking).png differ diff --git a/images/ASAAAA_Strain hardening exponent (n).png b/images/ASAAAA_Strain hardening exponent (n).png new file mode 100644 index 0000000000000000000000000000000000000000..b012e01fb0b12862ce1a2733d4a09f6c2bab27ce Binary files /dev/null and b/images/ASAAAA_Strain hardening exponent (n).png differ diff --git a/images/ASAAAA_Strength coefficient (K).png b/images/ASAAAA_Strength coefficient (K).png new file mode 100644 index 0000000000000000000000000000000000000000..61c0f27009d1cacc5f6c78dca8b16024afa49bed Binary files /dev/null and b/images/ASAAAA_Strength coefficient (K).png differ diff --git a/images/Abhijit.jpg b/images/Abhijit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..55f2f3360eba9d5446acb9e807e8a3a32d631851 Binary files /dev/null and b/images/Abhijit.jpg differ diff --git a/images/Cynate Ester + 55% Carbon Fiber_Moisture Absorption at Equilibrium.png b/images/Cynate Ester + 55% Carbon Fiber_Moisture Absorption at Equilibrium.png new file mode 100644 index 0000000000000000000000000000000000000000..b24afeed819ae1c25d1cd3bdc94ebf470d2331d5 Binary files /dev/null and b/images/Cynate Ester + 55% Carbon Fiber_Moisture Absorption at Equilibrium.png differ diff --git a/images/Epoxy + 44% Carbon fiber_Tensile Strength.png b/images/Epoxy + 44% Carbon fiber_Tensile Strength.png new file mode 100644 index 0000000000000000000000000000000000000000..63f6d520e6b49177dbd668e13278ae5b66e92a64 --- /dev/null +++ b/images/Epoxy + 44% Carbon fiber_Tensile Strength.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fd330279709453afa888090f43d4a14220b1b56e869d975a4c0015da74a29db +size 262420 diff --git a/images/GangLi.jpg b/images/GangLi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c6d175dc50f46ba2d3aa742031f49e839f136bfb Binary files /dev/null and b/images/GangLi.jpg differ diff --git a/images/Home.png b/images/Home.png new file mode 100644 index 0000000000000000000000000000000000000000..9c66ca218eb2e1a1f1962eba2e647e48daa51584 --- /dev/null +++ b/images/Home.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caf4fde646a560ceb5f0991f876626f2ff10afa9013854aa2b7720192bdbab69 +size 5922498 diff --git a/images/Materials_bg_InDeS.png b/images/Materials_bg_InDeS.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5020a446ba5c702af3cd1f00f6df5d772db8ad --- /dev/null +++ b/images/Materials_bg_InDeS.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:864115b3941abf1be1abfebfe7a5a5e1b4e1d98e9660ad923bc439835734dd22 +size 2483216 diff --git a/images/Mathias.jpg b/images/Mathias.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b9bfcc60869e03e122024825f129892fb43336db Binary files /dev/null and b/images/Mathias.jpg differ diff --git a/images/PTFE_Water Absorption.png b/images/PTFE_Water Absorption.png new file mode 100644 index 0000000000000000000000000000000000000000..bd933398578aefe9c30199ec913844f80e64551f Binary files /dev/null and b/images/PTFE_Water Absorption.png differ diff --git a/images/Pradeep.jpg b/images/Pradeep.jpg new file mode 100644 index 0000000000000000000000000000000000000000..05dfe80c2bf5d63ab3549de9fddbc3aab891fb5c Binary files /dev/null and b/images/Pradeep.jpg differ diff --git a/images/Stress-strain-response-of-the-T300-3k-carbon-fiber-bundle.png b/images/Stress-strain-response-of-the-T300-3k-carbon-fiber-bundle.png new file mode 100644 index 0000000000000000000000000000000000000000..2731614f97b3a250b10f6139247d274640b8acba Binary files /dev/null and b/images/Stress-strain-response-of-the-T300-3k-carbon-fiber-bundle.png differ diff --git a/images/Tejaswi.jpg b/images/Tejaswi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1eaa778034382057f6288313e3f7fa3e9937191a Binary files /dev/null and b/images/Tejaswi.jpg differ diff --git a/images/Tensile-stress-strain-curve-of-Pure-PEEK-fibres-for-three-different-extrusion.png b/images/Tensile-stress-strain-curve-of-Pure-PEEK-fibres-for-three-different-extrusion.png new file mode 100644 index 0000000000000000000000000000000000000000..b104fddb0e9bf5bd1e84895d1c364496a8bb2b3d Binary files /dev/null and b/images/Tensile-stress-strain-curve-of-Pure-PEEK-fibres-for-three-different-extrusion.png differ diff --git a/images/desktop.ini b/images/desktop.ini new file mode 100644 index 0000000000000000000000000000000000000000..2ac348efc59b7f6dc27d25b28276d0af16442198 --- /dev/null +++ b/images/desktop.ini @@ -0,0 +1,3 @@ +[LocalizedFileNames] +Screenshot 2026-01-20 001556.png=@Screenshot 2026-01-20 001556,0 +Screenshot 2026-01-20 001941.png=@Screenshot 2026-01-20 001941,0 diff --git a/images/iPP_Curve Error (Maximum).png b/images/iPP_Curve Error (Maximum).png new file mode 100644 index 0000000000000000000000000000000000000000..d96a4fb7eb5b56ae1addfdb5737f012af51327c2 --- /dev/null +++ b/images/iPP_Curve Error (Maximum).png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed7e365af4f15929472742db3d350caae98b55d736efe5005d7ffff12b627205 +size 631128 diff --git a/images/iPP_Feature-to-curve reconstruction normalized error.png b/images/iPP_Feature-to-curve reconstruction normalized error.png new file mode 100644 index 0000000000000000000000000000000000000000..acc808641ec3d885135138dc3aa6835e436dd657 Binary files /dev/null and b/images/iPP_Feature-to-curve reconstruction normalized error.png differ diff --git a/images/iPP_Injection Pressure (Pinject) Range.png b/images/iPP_Injection Pressure (Pinject) Range.png new file mode 100644 index 0000000000000000000000000000000000000000..18cd646bf2812b9bbaaed200e1205dc956db0159 --- /dev/null +++ b/images/iPP_Injection Pressure (Pinject) Range.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c1685904ff03b79898147b5aaf8adfb125f018526c3872cee48b85070dc9bf5 +size 1005013 diff --git a/images/iPP_Injection Rate (Rinject) Range.png b/images/iPP_Injection Rate (Rinject) Range.png new file mode 100644 index 0000000000000000000000000000000000000000..1e278d7be2aef16c28358620140094c8c3b6b970 --- /dev/null +++ b/images/iPP_Injection Rate (Rinject) Range.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc5611383d12d4e6d0678be2427b6ba8403d6dd5522a1480f048b7856286535 +size 1498423 diff --git a/images/iPP_Melting temperature.png b/images/iPP_Melting temperature.png new file mode 100644 index 0000000000000000000000000000000000000000..d5e08dbec5839eb567ca7ef13fff6beb8000bd0d --- /dev/null +++ b/images/iPP_Melting temperature.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56ee0d92f2479211f6372ca6e22844328915a3186e79c888eb0717d27b47c09f +size 875479 diff --git a/images/iPP_Normalized Root Mean Square Error (NRMSE) Distribution Center.png b/images/iPP_Normalized Root Mean Square Error (NRMSE) Distribution Center.png new file mode 100644 index 0000000000000000000000000000000000000000..f897cbefc34f9c1e5ed4ac1efca20920edc05289 --- /dev/null +++ b/images/iPP_Normalized Root Mean Square Error (NRMSE) Distribution Center.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:211f2dd7af79eaccc5fe86f6f374c3f21734557bd4a7f5b4ae5bf9bb0244a2d9 +size 476175 diff --git a/images/logo.png b/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0b9b3f17783a67c63cbaf36d9958e538e75b23cd --- /dev/null +++ b/images/logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca199c61238861f284e76397e24ace640bada75e6753ba4737023a1d8ad84aca +size 149859 diff --git a/images/us_deptenergy.jpg b/images/us_deptenergy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..475159845c0b41a69b51349f85365a0977d70e64 --- /dev/null +++ b/images/us_deptenergy.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92c687ec97c2ff1fc9bdaeab7d1e2896b1cf8cac65f1ae42cf39a392c5dbc427 +size 427509 diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0b9b3f17783a67c63cbaf36d9958e538e75b23cd --- /dev/null +++ b/logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca199c61238861f284e76397e24ace640bada75e6753ba4737023a1d8ad84aca +size 149859 diff --git a/merged_file.json b/merged_file.json new file mode 100644 index 0000000000000000000000000000000000000000..57f709c6727982611ea7a54fec32af45c585b985 --- /dev/null +++ b/merged_file.json @@ -0,0 +1,2579 @@ +[ + { + "material_abbreviation": "PTFE", + "material_name": "Polytetrafluoroethylene (PTFE), Molded", + "mechanical_properties": [ + { + "section": "Physical Properties", + "property_name": "Density", + "value": "0.700 - 3.35", + "english": "0.0253 - 0.121 lb/in³", + "comments": "Average value: 2.07 g/cc Grade Count:113", + "unit": "g/cc" + }, + { + "section": "Physical Properties", + "property_name": "Water Absorption", + "value": "0.000 - 0.0100", + "english": "0.000 - 0.0100 %", + "comments": "Average value: 0.00575% Grade Count:56", + "unit": "%" + }, + { + "section": "Physical Properties", + "property_name": "Particle Size", + "value": "0.550 - 4450", + "english": "0.550 - 4450 µm", + "comments": "Average value: 443 µm Grade Count:33", + "unit": "µm" + }, + { + "section": "Physical Properties", + "property_name": "Viscosity", + "value": "1.00e+13 - 1.00e+15", + "english": "1.00e+13 - 1.00e+15 cP", + "comments": "Average value: 5.05e+14 cP Grade Count:10", + "test_condition": "@Temperature 340 - 380 °C", + "unit": "cP" + }, + { + "section": "Physical Properties", + "property_name": "Linear Mold Shrinkage", + "value": "0.0200 - 0.0500", + "english": "0.0200 - 0.0500 in/in", + "comments": "Average value: 0.0317 cm/cm Grade Count:23", + "unit": "cm/cm" + }, + { + "section": "Mechanical Properties", + "property_name": "Hardness, Shore D", + "value": "11.0 - 70.0", + "english": "11.0 - 70.0", + "comments": "Average value: 57.0 Grade Count:42" + }, + { + "section": "Mechanical Properties", + "property_name": "Ball Indentation Hardness", + "value": "23.0 - 37.0", + "english": "3340 - 5370 psi", + "comments": "Average value: 32.0 MPa Grade Count:9", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Tensile Strength, Ultimate", + "value": "13.0 - 333", + "english": "1890 - 48300 psi", + "comments": "Average value: 35.3 MPa Grade Count:63", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Tensile Strength, Yield", + "value": "0.862 - 41.4", + "english": "125 - 6000 psi", + "comments": "Average value: 20.5 MPa Grade Count:49", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Elongation at Break", + "value": "70.0 - 650", + "english": "70.0 - 650 %", + "comments": "Average value: 308% Grade Count:113", + "unit": "%" + }, + { + "section": "Mechanical Properties", + "property_name": "Modulus of Elasticity", + "value": "0.392 - 0.750", + "english": "56.9 - 109 ksi", + "comments": "Average value: 0.521 GPa Grade Count:39", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Flexural Modulus", + "value": "0.490 - 0.700", + "english": "71.1 - 102 ksi", + "comments": "Average value: 0.531 GPa Grade Count:19", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Compressive Yield Strength", + "value": "1.50 - 28.6", + "english": "218 - 4150 psi", + "comments": "Average value: 9.82 MPa Grade Count:26", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Flex Crack Resistance", + "value": "1.00e+6", + "english": "1.00e+6", + "comments": "Average value: 1.00e+6 Grade Count:7" + }, + { + "section": "Mechanical Properties", + "property_name": "Shear Strength", + "value": "9.30792 - 25.5106", + "english": "1350.00 - 3700.01 psi", + "comments": "Average value: 15.1 MPa Grade Count:2", + "test_condition": "@Temperature 26.7 - 204 °C", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Izod Impact, Notched", + "value": "1.30 - 1.87", + "english": "2.44 - 3.50 ft-lb/in", + "comments": "Average value: 1.65 J/cm Grade Count:14", + "unit": "J/cm" + }, + { + "section": "Mechanical Properties", + "property_name": "Izod Impact, Unnotched", + "value": "1.60", + "english": "3.00 ft-lb/in", + "comments": "Average value: 1.60 J/cm Grade Count:10", + "unit": "J/cm" + }, + { + "section": "Mechanical Properties", + "property_name": "Charpy Impact, Notched", + "value": "1.40 - 1.90", + "english": "6.66 - 9.04 ft-lb/in²", + "comments": "Average value: 1.76 J/cm² Grade Count:5", + "unit": "J/cm²" + }, + { + "section": "Mechanical Properties", + "property_name": "Coefficient of Friction", + "value": "0.0200 - 0.280", + "english": "0.0200 - 0.280", + "comments": "Average value: 0.0625 Grade Count:43" + }, + { + "section": "Mechanical Properties", + "property_name": "Coefficient of Friction, Static", + "value": "0.0200 - 0.0800", + "english": "0.0200 - 0.0800", + "comments": "Average value: 0.0524 Grade Count:25" + }, + { + "section": "Mechanical Properties", + "property_name": "K (wear) Factor", + "value": "3.00 - 5040 x 10⁻⁸", + "english": "1.49 - 2500 x 10⁻¹⁰ in³-min/ft-lb-hr", + "comments": "Average value: 2180 x 10⁻⁸ mm³/N-M Grade Count:6", + "unit": "mm³/N-M" + }, + { + "section": "Mechanical Properties", + "property_name": "Taber Abrasion, mg/1000 Cycles", + "value": "5.00 - 41.8", + "english": "5.00 - 41.8", + "comments": "Average value: 21.5 Grade Count:10", + "unit": "mg" + }, + { + "section": "Mechanical Properties", + "property_name": "Compression Set", + "value": "3.00 - 20.1", + "english": "3.00 - 20.1 %", + "comments": "Average value: 11.7% Grade Count:6", + "unit": "%" + }, + { + "section": "Electrical Properties", + "property_name": "Electrical Resistivity", + "value": "1.00e+14 - 1.10e+18", + "english": "1.00e+14 - 1.10e+18 ohm-cm", + "comments": "Average value: 8.68e+17 ohm-cm Grade Count:49", + "unit": "ohm-cm" + }, + { + "section": "Electrical Properties", + "property_name": "Surface Resistance", + "value": "1.00e+13 - 1.00e+18", + "english": "1.00e+13 - 1.00e+18 ohm", + "comments": "Average value: 4.66e+17 ohm Grade Count:25", + "unit": "ohm" + }, + { + "section": "Electrical Properties", + "property_name": "Dielectric Constant", + "value": "2.00 - 12.0", + "english": "2.00 - 12.0", + "comments": "Average value: 2.39 Grade Count:56" + }, + { + "section": "Electrical Properties", + "property_name": "Dielectric Strength", + "value": "2.50 - 165", + "english": "63.5 - 4200 kV/in", + "comments": "Average value: 58.7 kV/mm Grade Count:74", + "unit": "kV/mm" + }, + { + "section": "Electrical Properties", + "property_name": "Dissipation Factor", + "value": "0.0000100 - 0.0360", + "english": "0.0000100 - 0.0360", + "comments": "Average value: 0.00119 Grade Count:53" + }, + { + "section": "Electrical Properties", + "property_name": "Dielectric Loss Index", + "value": "0.000100", + "english": "0.000100", + "comments": "Average value: 0.000100 Grade Count:6" + }, + { + "section": "Electrical Properties", + "property_name": "Arc Resistance", + "value": "300 - 420", + "english": "300 - 420 sec", + "comments": "Average value: 346 sec Grade Count:13", + "unit": "sec" + }, + { + "section": "Electrical Properties", + "property_name": "Comparative Tracking Index", + "value": "600", + "english": "600 V", + "comments": "Average value: 600 V Grade Count:6", + "unit": "V" + }, + { + "section": "Thermal Properties", + "property_name": "CTE, linear", + "value": "14.0 - 250", + "english": "7.78 - 139 µin/in-°F", + "comments": "Average value: 109 µm/m-°C Grade Count:43", + "unit": "µm/m-°C" + }, + { + "section": "Thermal Properties", + "property_name": "CTE, linear", + "value": "57.0 - 170", + "english": "31.7 - 94.4 µin/in-°F", + "comments": "Average value: 95.7 µm/m-°C Grade Count:9", + "test_condition": "@Temperature 30.0 - 300 °C", + "unit": "µm/m-°C" + }, + { + "section": "Thermal Properties", + "property_name": "Specific Heat Capacity", + "value": "1.00 - 1.05", + "english": "0.239 - 0.251 BTU/lb-°F", + "comments": "Average value: 1.01 J/g-°C Grade Count:6", + "unit": "J/g-°C" + }, + { + "section": "Thermal Properties", + "property_name": "Specific Heat Capacity", + "value": "0.960 - 1.03", + "english": "0.229 - 0.246 BTU/lb-°F", + "comments": "Average value: 0.995 J/g-°C Grade Count:1", + "test_condition": "@Temperature 0.000 - 50.0 °C", + "unit": "J/g-°C" + }, + { + "section": "Thermal Properties", + "property_name": "Thermal Conductivity", + "value": "0.230 - 0.500", + "english": "1.60 - 3.47 BTU-in/hr-ft²-°F", + "comments": "Average value: 0.263 W/m-K Grade Count:34", + "unit": "W/m-K" + }, + { + "section": "Thermal Properties", + "property_name": "Thermal Conductivity", + "value": "0.220 - 0.320", + "english": "1.53 - 2.22 BTU-in/hr-ft²-°F", + "comments": "Average value: 0.276 W/m-K Grade Count:7", + "unit": "W/m-K" + }, + { + "section": "Thermal Properties", + "property_name": "Melting Point", + "value": "135 - 344", + "english": "275 - 651 °F", + "comments": "Average value: 325 °C Grade Count:57", + "test_condition": "@Temperature 100 - 250 °C", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Maximum Service Temperature, Air", + "value": "93.3 - 316", + "english": "200 - 600 °F", + "comments": "Average value: 259 °C Grade Count:73", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Minimum Service Temperature, Air", + "value": "-268 - -200", + "english": "-450 - -328 °F", + "comments": "Average value: -212 °C Grade Count:23", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Flammability, UL94", + "value": "V-0", + "english": "V-0", + "comments": "Grade Count:58" + }, + { + "section": "Thermal Properties", + "property_name": "Oxygen Index", + "value": "92.0 - 95.0", + "english": "92.0 - 95.0 %", + "comments": "Average value: 94.9% Grade Count:30", + "unit": "%" + }, + { + "section": "Processing Properties", + "property_name": "Processing Temperature", + "value": "370", + "english": "698 °F", + "comments": "Average value: 370 °C Grade Count:5", + "unit": "°C" + }, + { + "section": "Processing Properties", + "property_name": "Moisture Content", + "value": "0.0400", + "english": "0.0400 %", + "comments": "Average value: 0.0400% Grade Count:4", + "unit": "%" + } + ] + }, + { + "material_abbreviation": "ABS", + "material_name": "Acrylonitrile Butadiene Styrene (ABS), Molded", + "mechanical_properties": [ + { + "property_name": "Density", + "value": "0.882 - 3.50", + "english": "0.0319 - 0.126 lb/in³", + "comments": "Average value: 1.07 g/cc Grade Count:774", + "section": "Physical Properties", + "test_condition": "", + "unit": "g/cc" + }, + { + "property_name": "Water Absorption", + "value": "0.0250 - 2.30", + "english": "0.0250 - 2.30 %", + "comments": "Average value: 0.383 % Grade Count:85", + "section": "Physical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Moisture Absorption at Equilibrium", + "value": "0.100 - 0.300", + "english": "0.100 - 0.300 %", + "comments": "Average value: 0.232 % Grade Count:26", + "section": "Physical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Water Absorption at Saturation", + "value": "0.00950 - 1.03", + "english": "0.00950 - 1.03 %", + "comments": "Average value: 0.561 % Grade Count:14", + "section": "Physical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Maximum Moisture Content", + "value": "0.0100 - 0.150", + "english": "0.0100 - 0.150", + "comments": "Average value: 0.0459 Grade Count:71", + "section": "Physical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Linear Mold Shrinkage", + "value": "0.000 - 0.0290", + "english": "0.000 - 0.0290 in/in", + "comments": "Average value: 0.00533 cm/cm Grade Count:581", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm/cm" + }, + { + "property_name": "Linear Mold Shrinkage, Transverse", + "value": "0.00200 - 0.00900", + "english": "0.00200 - 0.00900 in/in", + "comments": "Average value: 0.00522 cm/cm Grade Count:52", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm/cm" + }, + { + "property_name": "Melt Flow", + "value": "0.0800 - 125", + "english": "0.0800 - 125 g/10 min", + "comments": "Average value: 16.5 g/10 min Grade Count:805", + "section": "Physical Properties", + "test_condition": "", + "unit": "g/10 min" + }, + { + "property_name": "Hardness, Rockwell M", + "value": "53.0 - 92.0", + "english": "53.0 - 92.0", + "comments": "Average value: 71.8 Grade Count:4", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Hardness, Rockwell R", + "value": "13.0 - 122", + "english": "13.0 - 122", + "comments": "Average value: 108 Grade Count:431", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Hardness, Shore D", + "value": "79.0 - 103", + "english": "79.0 - 103", + "comments": "Average value: 87.0 Grade Count:3", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Ball Indentation Hardness", + "value": "70.0 - 120", + "english": "10200 - 17400 psi", + "comments": "Average value: 101 MPa Grade Count:68", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Ultimate", + "value": "2.60 - 73.1", + "english": "377 - 10600 psi", + "comments": "Average value: 41.0 MPa Grade Count:346", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Ultimate", + "value": "20.0 - 43.0", + "english": "2900 - 6240 psi", + "comments": "Average value: 31.8 MPa Grade Count:2", + "section": "Mechanical Properties", + "test_condition": "@Temperature 60.0 - 90.0 °C", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Yield", + "value": "2.00 - 77.0", + "english": "290 - 11200 psi", + "comments": "Average value: 44.8 MPa Grade Count:624", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Elongation at Break", + "value": "1.40 - 110", + "english": "1.40 - 110 %", + "comments": "Average value: 28.5 % Grade Count:493", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Elongation at Yield", + "value": "1.70 - 40.0", + "english": "1.70 - 40.0 %", + "comments": "Average value: 3.66 % Grade Count:189", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Modulus of Elasticity", + "value": "0.778 - 21.2", + "english": "113 - 3080 ksi", + "comments": "Average value: 2.35 GPa Grade Count:356", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "GPa" + }, + { + "property_name": "Flexural Yield Strength", + "value": "0.379 - 655", + "english": "55.0 - 95000 psi", + "comments": "Average value: 70.6 MPa Grade Count:626", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Flexural Modulus", + "value": "0.0241 - 6.89", + "english": "3.50 - 1000 ksi", + "comments": "Average value: 2.33 GPa Grade Count:699", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "GPa" + }, + { + "property_name": "Flexural Modulus", + "value": "1.50 - 4.00", + "english": "218 - 580 ksi", + "comments": "Average value: 2.84 GPa Grade Count:2", + "section": "Mechanical Properties", + "test_condition": "@Temperature 60.0 - 90.0 °C", + "unit": "GPa" + }, + { + "property_name": "Poissons Ratio", + "value": "0.360 - 0.380", + "english": "0.360 - 0.380", + "comments": "Average value: 0.364 Grade Count:5", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.100 - 7.85", + "english": "0.187 - 14.7 ft-lb/in", + "comments": "Average value: 2.41 J/cm Grade Count:516", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.350 - 3.00", + "english": "0.656 - 5.62 ft-lb/in", + "comments": "Average value: 1.00 J/cm Grade Count:23", + "section": "Mechanical Properties", + "test_condition": "@Temperature -50.0 - 0.000 °C", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.290 - 2.35", + "english": "0.543 - 4.40 ft-lb/in", + "comments": "Average value: 1.00 J/cm Grade Count:30", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 0.000 °C", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.290 - 2.35", + "english": "0.543 - 4.40 ft-lb/in", + "comments": "Average value: 1.00 J/cm Grade Count:30", + "section": "Mechanical Properties", + "test_condition": "@Thickness 3.17 - 12.7 mm", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Unnotched", + "value": "1.50 J/cm - NB", + "english": "2.81 ft-lb/in - NB", + "comments": "Average value: 8.79 J/cm Grade Count:27", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Unnotched", + "value": "0.600 - 6.47239", + "english": "1.12 - 12.1254 ft-lb/in", + "comments": "Average value: 1.24 J/cm Grade Count:4", + "section": "Mechanical Properties", + "test_condition": "@Temperature -30.0 - 0.125 °C", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Unnotched", + "value": "0.392266 - 4.8051", + "english": "0.734875 - 9.0019 ft-lb/in", + "comments": "Average value: 1.24 J/cm Grade Count:34", + "section": "Mechanical Properties", + "test_condition": "@Temperature -30.0 - -20.0 °C", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Unnotched", + "value": "0.392266 - 4.8051", + "english": "0.734875 - 9.0019 ft-lb/in", + "comments": "Average value: 1.24 J/cm Grade Count:34", + "section": "Mechanical Properties", + "test_condition": "@Thickness 3.20 - 6.40 mm", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched (ISO)", + "value": "1.00 - 45.0", + "english": "0.476 - 21.4 ft-lb/in²", + "comments": "Average value: 18.5 kJ/m² Grade Count:184", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "kJ/m²" + }, + { + "property_name": "Izod Impact, Notched (ISO)", + "value": "6.00 - 22.0", + "english": "2.86 - 10.5 ft-lb/in²", + "comments": "Average value: 9.13 kJ/m² Grade Count:61", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - -20.0 °C", + "unit": "kJ/m²" + }, + { + "property_name": "Izod Impact, Unnotched (ISO)", + "value": "6.00 - 6.00", + "english": "2.86 - 2.86 ft-lb/in²", + "comments": "Average value: 9.13 kJ/m² Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - -40.0 °C", + "unit": "kJ/m²" + }, + { + "property_name": "Izod Impact, Unnotched (ISO)", + "value": "6.00 - 6.00", + "english": "2.86 - 2.86 ft-lb/in²", + "comments": "Average value: 9.13 kJ/m² Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Thickness 4.00 - 4.00 mm", + "unit": "kJ/m²" + }, + { + "property_name": "Charpy Impact Unnotched", + "value": "39.2 kJ/m² NB", + "english": "18.7 ft-lb/in² - NB", + "comments": "Average value: 77.2 kJ/m² Grade Count:27", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "kJ/m²" + }, + { + "property_name": "Charpy Impact Unnotched", + "value": "60.0 - 70.0", + "english": "28.6 - 33.3 ft-lb/in²", + "comments": "Average value: 68.0 kJ/m² Grade Count:5", + "section": "Mechanical Properties", + "test_condition": "@Temperature -30.0 - -30.0 °C", + "unit": "kJ/m²" + }, + { + "property_name": "Charpy Impact Unnotched", + "value": "1.00 J/cm² NB", + "english": "4.76 ft-lb/in² - NB", + "comments": "Average value: 11.4 J/cm² Grade Count:145", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm²" + }, + { + "property_name": "Charpy Impact Unnotched", + "value": "0.300 J/cm² NB", + "english": "1.43 ft-lb/in² - NB", + "comments": "Average value: 7.48 J/cm² Grade Count:96", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - -20.0 °C", + "unit": "J/cm²" + }, + { + "property_name": "Charpy Impact, Notched", + "value": "0.100 - 14.0", + "english": "0.476 - 66.6 ft-lb/in²", + "comments": "Average value: 1.90 J/cm² Grade Count:292", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm²" + }, + { + "property_name": "Charpy Impact, Notched", + "value": "0.200 - 2.40", + "english": "0.952 - 11.4 ft-lb/in²", + "comments": "Average value: 0.877 J/cm² Grade Count:103", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - -20.0 °C", + "unit": "J/cm²" + }, + { + "property_name": "Gardner Impact", + "value": "1.80 - 22.6", + "english": "1.33 - 16.7 ft-lb", + "comments": "Average value: 15.9 J Grade Count:7", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J" + }, + { + "property_name": "Dart Drop, Total Energy", + "value": "2.37 - 46.3", + "english": "1.75 - 34.1 ft-lb", + "comments": "Average value: 28.0 J Grade Count:5", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J" + }, + { + "property_name": "Dart Drop, Total Energy", + "value": "32.8 - 32.8", + "english": "24.2 - 24.2 ft-lb", + "comments": "Average value: 35.8 J Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature -18.0 - -18.0 °C", + "unit": "J" + }, + { + "property_name": "Dart Drop, Total Energy", + "value": "37.3 - 37.3", + "english": "27.5 - 27.5 ft-lb", + "comments": "Average value: 35.8 J Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature -18.0 - -18.0 °C", + "unit": "J" + }, + { + "property_name": "Dart Drop, Total Energy", + "value": "37.3 - 37.3", + "english": "27.5 - 27.5 ft-lb", + "comments": "Average value: 35.8 J Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Thickness 3.20 - 3.20 mm", + "unit": "J" + }, + { + "property_name": "Falling Dart Impact", + "value": "2.82 - 37.6", + "english": "2.08 - 27.7 ft-lb", + "comments": "Average value: 25.3 J Grade Count:9", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J" + }, + { + "property_name": "Instrumented Impact Total Energy", + "value": "5.40 - 54.0", + "english": "3.98 - 39.8 ft-lb", + "comments": "Average value: 39.6 J Grade Count:21", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J" + }, + { + "property_name": "Tensile Creep Modulus, 1 hour", + "value": "2200 - 2800", + "english": "319000 - 406000 psi", + "comments": "Average value: 2480 MPa Grade Count:4", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Creep Modulus, 1000 hours", + "value": "1250 - 1900", + "english": "181000 - 276000 psi", + "comments": "Average value: 1640 MPa Grade Count:4", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Electrical Resistivity", + "value": "1500 - 1.00e+18", + "english": "1500 - 1.00e+18 ohm-cm", + "comments": "Average value: 1.87e+16 ohm-cm Grade Count:127", + "section": "Electrical Properties", + "test_condition": "", + "unit": "ohm-cm" + }, + { + "property_name": "Surface Resistance", + "value": "1000 - 2.00e+17", + "english": "1000 - 2.00e+17 ohm", + "comments": "Average value: 6.94e+15 ohm Grade Count:120", + "section": "Electrical Properties", + "test_condition": "", + "unit": "ohm" + }, + { + "property_name": "Static Decay", + "value": "0.250 - 3.00", + "english": "0.250 - 3.00 sec", + "comments": "Average value: 1.24 sec Grade Count:7", + "section": "Electrical Properties", + "test_condition": "", + "unit": "sec" + }, + { + "property_name": "Dielectric Constant", + "value": "2.70 - 3.80", + "english": "2.70 - 3.80", + "comments": "Average value: 2.98 Grade Count:54", + "section": "Electrical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Dielectric Strength", + "value": "15.7 - 53.0", + "english": "400 - 1350 kV/in", + "comments": "Average value: 32.0 kV/mm Grade Count:80", + "section": "Electrical Properties", + "test_condition": "", + "unit": "kV/mm" + }, + { + "property_name": "Dissipation Factor", + "value": "0.00400 - 0.0900", + "english": "0.00400 - 0.0900", + "comments": "Average value: 0.00949 Grade Count:52", + "section": "Electrical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Arc Resistance", + "value": "0.000 - 180", + "english": "0.000 - 180 sec", + "comments": "Average value: 78.8 sec Grade Count:28", + "section": "Electrical Properties", + "test_condition": "", + "unit": "sec" + }, + { + "property_name": "Comparative Tracking Index", + "value": "92.0 - 600", + "english": "92.0 - 600 V", + "comments": "Average value: 561 V Grade Count:93", + "section": "Electrical Properties", + "test_condition": "", + "unit": "V" + }, + { + "property_name": "Hot Wire Ignition, HWI", + "value": "7.00 - 120", + "english": "7.00 - 120 sec", + "comments": "Average value: 26.9 sec Grade Count:33", + "section": "Electrical Properties", + "test_condition": "", + "unit": "sec" + }, + { + "property_name": "High Amp Arc Ignition, HAI", + "value": "30.0 - 200", + "english": "30.0 - 200 arcs", + "comments": "Average value: 123 arcs Grade Count:33", + "section": "Electrical Properties", + "test_condition": "", + "unit": "arcs" + }, + { + "property_name": "High Voltage Arc-Tracking Rate, HVTR", + "value": "0.000 - 150", + "english": "0.000 - 5.91 in/min", + "comments": "Average value: 34.4 mm/min Grade Count:31", + "section": "Electrical Properties", + "test_condition": "", + "unit": "mm/min" + }, + { + "property_name": "CTE, linear", + "value": "7.90 - 139", + "english": "4.39 - 77.2 µin/in-°F", + "comments": "Average value: 83.4 µm/m-°C Grade Count:189", + "section": "Thermal Properties", + "test_condition": "", + "unit": "µm/m-°C" + }, + { + "property_name": "CTE, linear, Transverse to Flow", + "value": "81.0 - 100", + "english": "45.0 - 55.6 µin/in-°F", + "comments": "Average value: 91.4 µm/m-°C Grade Count:15", + "section": "Thermal Properties", + "test_condition": "", + "unit": "µm/m-°C" + }, + { + "property_name": "Specific Heat Capacity", + "value": "1.60 - 2.13", + "english": "0.382 - 0.509 BTU/lb-°F", + "comments": "Average value: 1.99 J/g-°C Grade Count:7", + "section": "Thermal Properties", + "test_condition": "", + "unit": "J/g-°C" + }, + { + "property_name": "Thermal Conductivity", + "value": "0.128 - 0.187", + "english": "0.888 - 1.30 BTU-in/hr-ft²-°F", + "comments": "Average value: 0.162 W/m-K Grade Count:19", + "section": "Thermal Properties", + "test_condition": "", + "unit": "W/m-K" + }, + { + "property_name": "Thermal Conductivity", + "value": "0.250 - 0.250", + "english": "1.74 - 1.74 BTU-in/hr-ft²-°F", + "comments": "Average value: 0.250 W/m-K Grade Count:1", + "section": "Thermal Properties", + "test_condition": "@Temperature 30.0 - 260 °C", + "unit": "W/m-K" + }, + { + "property_name": "Maximum Service Temperature, Air", + "value": "50.0 - 109", + "english": "122 - 228 °F", + "comments": "Average value: 74.9 °C Grade Count:24", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Hot Ball Pressure Test", + "value": "75.0 - 105", + "english": "167 - 221 °F", + "comments": "Average value: 88.0 °C Grade Count:27", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Deflection Temperature at 0.46 MPa (66 psi)", + "value": "56.0 - 165", + "english": "133 - 329 °F", + "comments": "Average value: 95.3 °C Grade Count:256", + "section": "Thermal Properties", + "test_condition": "0.46 MPa (66 psi)", + "unit": "°C" + }, + { + "property_name": "Deflection Temperature at 1.8 MPa (264 psi)", + "value": "63.9 - 220", + "english": "147 - 428 °F", + "comments": "Average value: 89.0 °C Grade Count:702", + "section": "Thermal Properties", + "test_condition": "1.8 MPa (264 psi)", + "unit": "°C" + }, + { + "property_name": "Vicat Softening Point", + "value": "45.0 - 160", + "english": "113 - 320 °F", + "comments": "Average value: 99.5 °C Grade Count:571", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Heat Distortion Temperature", + "value": "76.4 - 87.8", + "english": "170 - 190 °F", + "comments": "Average value: 84.2 °C Grade Count:6", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Glass Transition Temp, Tg", + "value": "105 - 109", + "english": "221 - 228 °F", + "comments": "Average value: 107 °C Grade Count:9", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "UL RTI Electrical", + "value": "50.0 - 120", + "english": "122 - 248 °F", + "comments": "Average value: 73.4 °C Grade Count:94", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "UL RTI, Mechanical with Impact", + "value": "50.0 - 105", + "english": "122 - 221 °F", + "comments": "Average value: 71.1 °C Grade Count:85", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "UL RTI, Mechanical without Impact", + "value": "50.0 - 120", + "english": "122 - 248 °F", + "comments": "Average value: 73.2 °C Grade Count:84", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Flammability, UL94", + "value": "HB - 5VA", + "english": "HB - 5VA", + "comments": "Average value: 1.50 Grade Count:511", + "section": "Thermal Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Oxygen Index", + "value": "19.0 - 30.0", + "english": "19.0 - 30.0 %", + "comments": "Average value: 21.6 % Grade Count:5", + "section": "Thermal Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Glow Wire Test", + "value": "600 - 960", + "english": "1110 - 1760 °F", + "comments": "Average value: 673 °C Grade Count:49", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Haze", + "value": "2.00 - 4.00", + "english": "2.00 - 4.00 %", + "comments": "Average value: 2.74 % Grade Count:7", + "section": "Optical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Gloss", + "value": "30.0 - 98.0", + "english": "30.0 - 98.0 %", + "comments": "Average value: 84.0 % Grade Count:57", + "section": "Optical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Yellow Index", + "value": "-1.40 - 24.3", + "english": "-1.40 - 24.3 %", + "comments": "Average value: 14.2 % Grade Count:5", + "section": "Optical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Transmission, Visible", + "value": "0.000 - 90.0", + "english": "0.000 - 90.0 %", + "comments": "Average value: 68.8 % Grade Count:51", + "section": "Optical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Processing Temperature", + "value": "170 - 270", + "english": "338 - 518 °F", + "comments": "Average value: 212 °C Grade Count:87", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Nozzle Temperature", + "value": "180 - 310", + "english": "356 - 590 °F", + "comments": "Average value: 238 °C Grade Count:172", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Adapter Temperature", + "value": "200 - 300", + "english": "392 - 572 °F", + "comments": "Average value: 277 °C Grade Count:31", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Die Temperature", + "value": "200 - 295", + "english": "392 - 563 °F", + "comments": "Average value: 267 °C Grade Count:31", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Melt Temperature", + "value": "149 - 323", + "english": "300 - 613 °F", + "comments": "Average value: 234 °C Grade Count:315", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Mold Temperature", + "value": "10.0 - 120", + "english": "50.0 - 248 °F", + "comments": "Average value: 62.3 °C Grade Count:377", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Injection Velocity", + "value": "200 - 240", + "english": "7.87 - 9.45 in/sec", + "comments": "Average value: 232 mm/sec Grade Count:30", + "section": "Processing Properties", + "test_condition": "", + "unit": "mm/sec" + }, + { + "property_name": "Roll Temperature", + "value": "60.0 - 150", + "english": "140 - 302 °F", + "comments": "Average value: 127 °C Grade Count:29", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Drying Temperature", + "value": "60.0 - 120", + "english": "140 - 248 °F", + "comments": "Average value: 84.2 °C Grade Count:381", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Moisture Content", + "value": "0.0100 - 0.300", + "english": "0.0100 - 0.300 %", + "comments": "Average value: 0.0668 % Grade Count:115", + "section": "Processing Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Dew Point", + "value": "-29.0 - 17.8", + "english": "-20.2 - 0.000 °F", + "comments": "Average value: -19.6 °C Grade Count:6", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Injection Pressure", + "value": "4.14 - 130", + "english": "600 - 18900 psi", + "comments": "Average value: 56.9 MPa Grade Count:35", + "section": "Processing Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Vent Depth", + "value": "0.00254 - 0.0510", + "english": "0.00100 - 0.0201 in", + "comments": "Average value: 0.0123 cm Grade Count:10", + "section": "Processing Properties", + "test_condition": "", + "unit": "cm" + } + ] + }, + { + "material_abbreviation": "PEKK", + "material_name": "Polyetherketoneketone (PEKK), Unreinforced", + "mechanical_properties": [ + { + "section": "Physical Properties", + "property_name": "Density", + "value": "1.27 - 1.31", + "english": "0.0459 - 0.0473 lb/in³", + "comments": "Average value: 1.28 g/cc Grade Count:20", + "unit": "g/cc" + }, + { + "section": "Physical Properties", + "property_name": "Water Absorption", + "value": "0.100 - 0.600", + "english": "0.100 - 0.600 %", + "comments": "Average value: 0.260 % Grade Count:5", + "unit": "%" + }, + { + "section": "Physical Properties", + "property_name": "Moisture Absorption at Equilibrium", + "value": "0.150 - 0.600", + "english": "0.150 - 0.600 %", + "comments": "Average value: 0.300 % Grade Count:3", + "unit": "%" + }, + { + "section": "Physical Properties", + "property_name": "Linear Mold Shrinkage", + "value": "0.00500 - 0.0160", + "english": "0.00500 - 0.0160 in/in", + "comments": "Average value: 0.00950 cm/cm Grade Count:4", + "unit": "cm/cm" + }, + { + "section": "Physical Properties", + "property_name": "Melt Flow", + "value": "6.00 - 120", + "english": "6.00 - 120 g/10 min", + "comments": "Average value: 30.3 g/10 min Grade Count:12", + "unit": "g/10 min" + }, + { + "section": "Mechanical Properties", + "property_name": "Hardness, Rockwell M", + "value": "86.0 - 88.0", + "english": "86.0 - 88.0", + "comments": "Average value: 87.3 Grade Count:3", + "unit": "" + }, + { + "section": "Mechanical Properties", + "property_name": "Tensile Strength, Ultimate", + "value": "40.6 - 111", + "english": "5890 - 16100 psi", + "comments": "Average value: 76.2 MPa Grade Count:13", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Tensile Strength, Ultimate", + "value": "25.9 - 116.5", + "english": "3760 - 16900 psi", + "comments": "Average value: 68.0 MPa Grade Count:1", + "test_condition": "@Temperature -184 - 149 °C", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Tensile Strength, Yield", + "value": "40.6 - 95.0", + "english": "5890 - 13800 psi", + "comments": "Average value: 78.7 MPa Grade Count:5", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Elongation at Break", + "value": "0.940 - 80.0", + "english": "0.940 - 80.0 %", + "comments": "Average value: 13.4 % Grade Count:13", + "unit": "%" + }, + { + "section": "Mechanical Properties", + "property_name": "Elongation at Break", + "value": "1.20 - 23.5", + "english": "1.20 - 23.5 %", + "comments": "Average value: 5.76 % Grade Count:1", + "test_condition": "@Temperature -184 - 149 °C", + "unit": "%" + }, + { + "section": "Mechanical Properties", + "property_name": "Elongation at Yield", + "value": "0.580 - 4.89", + "english": "0.580 - 4.89 %", + "comments": "Average value: 3.61 % Grade Count:5", + "unit": "%" + }, + { + "section": "Mechanical Properties", + "property_name": "Modulus of Elasticity", + "value": "2.68 - 6.56", + "english": "389 - 952 ksi", + "comments": "Average value: 3.61 GPa Grade Count:20", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Modulus of Elasticity", + "value": "1.44 - 8.074", + "english": "209.0 - 1171 ksi", + "comments": "Average value: 5.03 GPa Grade Count:1", + "test_condition": "@Temperature -184 - 149 °C", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Flexural Yield Strength", + "value": "54.0 - 193", + "english": "7830 - 28000 psi", + "comments": "Average value: 124 MPa Grade Count:12", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Flexural Modulus", + "value": "2.50 - 5.54", + "english": "363 - 804 ksi", + "comments": "Average value: 3.37 GPa Grade Count:12", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Flexural Strain at Break", + "value": "1.40 - 6.00", + "english": "1.40 - 6.00 %", + "comments": "Average value: 3.26 % Grade Count:7", + "unit": "%" + }, + { + "section": "Mechanical Properties", + "property_name": "Compressive Yield Strength", + "value": "90.6 - 207", + "english": "13100 - 30000 psi", + "comments": "Average value: 117 MPa Grade Count:9", + "unit": "MPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Compressive Modulus", + "value": "2.12 - 5.86", + "english": "307 - 850 ksi", + "comments": "Average value: 2.86 GPa Grade Count:6", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Poissons Ratio", + "value": "0.330 - 0.450", + "english": "0.330 - 0.450", + "comments": "Average value: 0.390 Grade Count:4", + "unit": "" + }, + { + "section": "Mechanical Properties", + "property_name": "Shear Modulus", + "value": "1.17 - 1.57", + "english": "170 - 228 ksi", + "comments": "Average value: 1.44 GPa Grade Count:3", + "unit": "GPa" + }, + { + "section": "Mechanical Properties", + "property_name": "Izod Impact, Notched", + "value": "0.220 - 0.630", + "english": "0.412 - 1.18 ft-lb/in", + "comments": "Average value: 0.399 J/cm Grade Count:8", + "unit": "J/cm" + }, + { + "section": "Mechanical Properties", + "property_name": "Izod Impact, Unnotched", + "value": "0.470 - 30.8", + "english": "0.881 - 57.7 ft-lb/in", + "comments": "Average value: 8.66 J/cm Grade Count:5", + "unit": "J/cm" + }, + { + "section": "Mechanical Properties", + "property_name": "Charpy Impact Unnotched", + "value": "2.20", + "english": "10.5 ft-lb/in²", + "comments": "Average value: 4.87 J/cm² Grade Count:8", + "test_condition": "NB", + "unit": "J/cm²" + }, + { + "section": "Mechanical Properties", + "property_name": "Charpy Impact Unnotched", + "value": "1.80", + "english": "8.57 ft-lb/in²", + "comments": "Average value: 3.72 J/cm² Grade Count:7", + "test_condition": "NB @Temperature -30.0 - 30.0 °C", + "unit": "J/cm²" + }, + { + "section": "Mechanical Properties", + "property_name": "Charpy Impact, Notched", + "value": "0.450 - 0.550", + "english": "2.14 - 2.62 ft-lb/in²", + "comments": "Average value: 0.488 J/cm² Grade Count:8", + "unit": "J/cm²" + }, + { + "section": "Mechanical Properties", + "property_name": "Charpy Impact, Notched", + "value": "0.400 - 0.500", + "english": "1.90 - 2.38 ft-lb/in²", + "comments": "Average value: 0.457 J/cm² Grade Count:7", + "test_condition": "@Temperature -30.0 - 30.0 °C", + "unit": "J/cm²" + }, + { + "section": "Mechanical Properties", + "property_name": "Coefficient of Friction", + "value": "0.173 - 0.186", + "english": "0.173 - 0.186", + "comments": "Average value: 0.177 Grade Count:3", + "unit": "" + }, + { + "section": "Mechanical Properties", + "property_name": "Coefficient of Friction, Static", + "value": "0.262 - 0.285", + "english": "0.262 - 0.285", + "comments": "Average value: 0.270 Grade Count:3", + "unit": "" + }, + { + "section": "Electrical Properties", + "property_name": "Electrical Resistivity", + "value": "3820 - 1.00e+16", + "english": "3820 - 1.00e+16 ohm-cm", + "comments": "Average value: 2.73e+15 ohm-cm Grade Count:10", + "unit": "ohm-cm" + }, + { + "section": "Electrical Properties", + "property_name": "Surface Resistance", + "value": "8.43 - 2.00e+16", + "english": "8.43 - 2.00e+16 ohm", + "comments": "Average value: 8.57e+15 ohm Grade Count:6", + "unit": "ohm" + }, + { + "section": "Electrical Properties", + "property_name": "Dielectric Constant", + "value": "2.50 - 3.32", + "english": "2.50 - 3.32", + "comments": "Average value: 2.95 Grade Count:13", + "unit": "" + }, + { + "section": "Electrical Properties", + "property_name": "Dielectric Strength", + "value": "23.6 - 84.0", + "english": "599 - 2130 kV/in", + "comments": "Average value: 47.8 kV/mm Grade Count:4", + "unit": "kV/mm" + }, + { + "section": "Electrical Properties", + "property_name": "Dissipation Factor", + "value": "0.00300 - 0.00400", + "english": "0.00300 - 0.00400", + "comments": "Average value: 0.00363 Grade Count:6", + "unit": "" + }, + { + "section": "Thermal Properties", + "property_name": "CTE, linear", + "value": "21.0 - 77.0", + "english": "11.7 - 42.8 µin/in-°F", + "comments": "Average value: 42.8 µm/m-°C Grade Count:5", + "unit": "µm/m-°C" + }, + { + "section": "Thermal Properties", + "property_name": "CTE, linear", + "value": "34.74 - 59.22", + "english": "19.30 - 32.90 µin/in-°F", + "comments": "Average value: 46.5 µm/m-°C Grade Count:4", + "test_condition": "@Temperature 30.0 - 150 °C", + "unit": "µm/m-°C" + }, + { + "section": "Thermal Properties", + "property_name": "Thermal Conductivity", + "value": "0.250 - 0.470", + "english": "1.74 - 3.26 BTU-in/hr-ft²-°F", + "comments": "Average value: 0.329 W/m-K Grade Count:4", + "unit": "W/m-K" + }, + { + "section": "Thermal Properties", + "property_name": "Melting Point", + "value": "300 - 360", + "english": "572 - 680 °F", + "comments": "Average value: 318 °C Grade Count:7", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Maximum Service Temperature, Air", + "value": "149 - 300", + "english": "300 - 572 °F", + "comments": "Average value: 228 °C Grade Count:7", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Deflection Temperature at 0.46 MPa", + "value": "150 - 277", + "english": "302 - 531 °F", + "comments": "Average value: 170 °C Grade Count:7", + "test_condition": "0.46 MPa (66 psi)", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Deflection Temperature at 1.8 MPa", + "value": "139 - 185", + "english": "282 - 365 °F", + "comments": "Average value: 157 °C Grade Count:18", + "test_condition": "1.8 MPa (264 psi)", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Glass Transition Temp, Tg", + "value": "149 - 160", + "english": "300 - 320 °F", + "comments": "Average value: 156 °C Grade Count:11", + "unit": "°C" + }, + { + "section": "Thermal Properties", + "property_name": "Flammability, UL94", + "value": "V-0", + "english": "V-0", + "comments": "Grade Count:5", + "unit": "" + }, + { + "section": "Thermal Properties", + "property_name": "Smoke Density", + "value": "10.0", + "english": "10.0", + "comments": "Average value: 10.0 Grade Count:3", + "unit": "" + }, + { + "section": "Thermal Properties", + "property_name": "Oxygen Index", + "value": "35.0 - 40.0", + "english": "35.0 - 40.0 %", + "comments": "Average value: 37.5 % Grade Count:11", + "unit": "%" + } + ] + }, + { + "material_abbreviation": "PA66", + "material_name": "Nylon 66, Unreinforced", + "mechanical_properties": [ + { + "property_name": "Density", + "value": "1.02 - 2.70", + "english": "0.0368 - 0.0975 lb/in³", + "comments": "Average value: 1.17 g/cc Grade Count:517", + "section": "Physical Properties", + "test_condition": "", + "unit": "g/cc" + }, + { + "property_name": "Water Absorption", + "value": "0.300 - 9.50", + "english": "0.300 - 9.50%", + "comments": "Average value: 3.51% Grade Count:226", + "section": "Physical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Moisture Absorption at Equilibrium", + "value": "0.170 - 8.50", + "english": "0.170 - 8.50%", + "comments": "Average value: 2.47% Grade Count:168", + "section": "Physical Properties", + "test_condition": "" + }, + { + "property_name": "Moisture Absorption at Equilibrium", + "value": "2.90 - 3.10", + "english": "2.90 - 3.10%", + "comments": "Average value: 3.00% Grade Count:6", + "section": "Physical Properties", + "test_condition": "@Temperature 70.0 - 70.0 °C", + "unit": "%" + }, + { + "property_name": "Water Absorption at Saturation", + "value": "1.70 - 9.00", + "english": "1.70 - 9.00%", + "comments": "Average value: 6.44% Grade Count:99", + "section": "Physical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Viscosity", + "value": "2.48 - 50.0", + "english": "2.48 - 50.0 cP", + "comments": "Average value: 9.44 cP Grade Count:7", + "section": "Physical Properties", + "test_condition": "", + "unit": "cP" + }, + { + "property_name": "Viscosity", + "value": "13.0 - 700000", + "english": "13.0 - 700000 cP", + "comments": "Average value: 122000 cP Grade Count:12", + "section": "Physical Properties", + "test_condition": "", + "unit": "cP" + }, + { + "property_name": "Viscosity", + "value": "13.0 - 700000", + "english": "13.0 - 700000 cP", + "comments": "Average value: 122000 cP Grade Count:12", + "section": "Physical Properties", + "test_condition": "@Temperature 200 - 300 °C", + "unit": "cP" + }, + { + "property_name": "Viscosity", + "value": "13.0 - 700000", + "english": "13.0 - 700000 cP", + "comments": "", + "section": "Physical Properties", + "test_condition": "@Shear Rate 30.0 - 10000 1/s", + "unit": "cP" + }, + { + "property_name": "Viscosity Test", + "value": "115 - 160", + "english": "1.15 - 1.60 dl/g", + "comments": "Average value: 141 cm³/g Grade Count:7", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm³/g" + }, + { + "property_name": "Viscosity Number", + "value": "140 - 330", + "english": "1.40 - 3.30 dl/g", + "comments": "Average value: 199 cm³/g Grade Count:15", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm³/g" + }, + { + "property_name": "Maximum Moisture Content", + "value": "0.0200 - 0.200", + "english": "0.0200 - 0.200", + "comments": "Average value: 0.139 Grade Count:29", + "section": "Physical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Maximum Moisture Content", + "value": "0.200 - 0.200", + "english": "0.200 - 0.200", + "comments": "Average value: 0.200 Grade Count:1", + "section": "Physical Properties", + "test_condition": "@Temperature 120 - 120 °C", + "unit": "" + }, + { + "property_name": "Maximum Moisture Content", + "value": "0.200 - 0.200", + "english": "0.200 - 0.200", + "comments": "Average value: 0.200 Grade Count:1", + "section": "Physical Properties", + "test_condition": "@Time 299 - 299 sec", + "unit": "" + }, + { + "property_name": "Linear Mold Shrinkage", + "value": "0.00100 - 0.0310", + "english": "0.00100 - 0.0310 in/in", + "comments": "Average value: 0.0139 cm/cm Grade Count:337", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm/cm" + }, + { + "property_name": "Linear Mold Shrinkage, Transverse", + "value": "0.00160 - 0.0740", + "english": "0.00160 - 0.0740 in/in", + "comments": "Average value: 0.0157 cm/cm Grade Count:156", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm/cm" + }, + { + "property_name": "Melt Flow", + "value": "2.00 - 180", + "english": "2.00 - 180 g/10 min", + "comments": "Average value: 52.5 g/10 min Grade Count:56", + "section": "Physical Properties", + "test_condition": "", + "unit": "g/10 min" + }, + { + "property_name": "Spiral Flow", + "value": "104 - 105", + "english": "40.9 - 41.3 in", + "comments": "Average value: 104 cm Grade Count:4", + "section": "Physical Properties", + "test_condition": "", + "unit": "cm" + }, + { + "property_name": "Hardness, Rockwell M", + "value": "55.0 - 90.0", + "english": "55.0 - 90.0", + "comments": "Average value: 75.8 Grade Count:31", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Hardness, Rockwell R", + "value": "78.0 - 121", + "english": "78.0 - 121", + "comments": "Average value: 114 Grade Count:82", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Hardness, Rockwell R", + "value": "97.0 - 97.0", + "english": "97.0 - 97.0", + "comments": "Average value: 97.0 Grade Count:3", + "section": "Mechanical Properties", + "test_condition": "@Temperature 80.0 - 80.0 °C", + "unit": "" + }, + { + "property_name": "Hardness, Shore D", + "value": "76.0 - 88.0", + "english": "76.0 - 88.0", + "comments": "Average value: 82.2 Grade Count:15", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Ball Indentation Hardness", + "value": "70.0 - 180", + "english": "10200 - 26100 psi", + "comments": "Average value: 137 MPa Grade Count:12", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Ultimate", + "value": "7.52 - 260", + "english": "1090 - 37700 psi", + "comments": "Average value: 81.2 MPa Grade Count:274", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Ultimate", + "value": "10.0 - 98.0", + "english": "1450 - 14200 psi", + "comments": "Average value: 76.8 MPa Grade Count:6", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 80.0 °C", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Yield", + "value": "28.0 - 116", + "english": "4060 - 16800 psi", + "comments": "Average value: 72.1 MPa Grade Count:298", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Strength, Yield", + "value": "44.8159 - 44.8159", + "english": "6500.01 - 6500.01 psi", + "comments": "Average value: 44.8 MPa Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature 77.0 - 77.0 °C", + "unit": "MPa" + }, + { + "property_name": "Elongation at Break", + "value": "0.000 - 300", + "english": "0.000 - 300%", + "comments": "Average value: 47.6% Grade Count:436", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Elongation at Break", + "value": ">= 100", + "english": ">= 100%", + "comments": "Average value: 100% Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature 60.0 - 150 °C", + "unit": "%" + }, + { + "property_name": "Elongation at Yield", + "value": "1.50 - 210", + "english": "1.50 - 210%", + "comments": "Average value: 13.9% Grade Count:220", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Elongation at Yield", + "value": "30.0 - 30.0", + "english": "30.0 - 30.0%", + "comments": "Average value: 30.0% Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature 76.7 - 76.7 °C", + "unit": "%" + }, + { + "property_name": "Modulus of Elasticity", + "value": "0.600 - 25.0", + "english": "87.0 - 3630 ksi", + "comments": "Average value: 3.47 GPa Grade Count:346", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "GPa" + }, + { + "property_name": "Modulus of Elasticity", + "value": "0.350 - 1.90", + "english": "50.8 - 276 ksi", + "comments": "Average value: 0.850 GPa Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature 60.0 - 150 °C", + "unit": "GPa" + }, + { + "property_name": "Flexural Yield Strength", + "value": "11.0 - 350", + "english": "1600 - 50800 psi", + "comments": "Average value: 102 MPa Grade Count:361", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Flexural Yield Strength", + "value": "29.9922 - 140", + "english": "4350.01 - 20300 psi", + "comments": "Average value: 85.3 MPa Grade Count:7", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 90.0 °C", + "unit": "MPa" + }, + { + "property_name": "Flexural Modulus", + "value": "0.600 - 18.5", + "english": "87.0 - 2680 ksi", + "comments": "Average value: 3.01 GPa Grade Count:413", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "GPa" + }, + { + "property_name": "Flexural Modulus", + "value": "0.500 - 4.30", + "english": "72.5 - 624 ksi", + "comments": "Average value: 2.27 GPa Grade Count:7", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 90.0 °C", + "unit": "GPa" + }, + { + "property_name": "Flexural Strain at Break", + "value": "5.50 - 9.00", + "english": "5.50 - 9.00%", + "comments": "Average value: 7.38% Grade Count:12", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Compressive Yield Strength", + "value": "7.00 - 100", + "english": "1020 - 14500 psi", + "comments": "Average value: 57.6 MPa Grade Count:14", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Poissons Ratio", + "value": "0.380 - 0.450", + "english": "0.380 - 0.450", + "comments": "Average value: 0.400 Grade Count:22", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Shear Strength", + "value": "50.0 - 80.0", + "english": "7250 - 11600 psi", + "comments": "Average value: 70.3 MPa Grade Count:9", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.300 - 8.54", + "english": "0.562 - 16.0 ft-lb/in", + "comments": "Average value: 1.04 J/cm Grade Count:154", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.240 - 0.480", + "english": "0.450 - 0.899 ft-lb/in", + "comments": "Average value: 0.385 J/cm Grade Count:8", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 20.0 °C", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.490332 - 0.490332", + "english": "0.918594 - 0.918594 ft-lb/in", + "comments": "Average value: 0.385 J/cm Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Temperature 3.20 - 3.20 °C", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched", + "value": "0.490332 - 0.490332", + "english": "0.918594 - 0.918594 ft-lb/in", + "comments": "Average value: 0.385 J/cm Grade Count:1", + "section": "Mechanical Properties", + "test_condition": "@Thickness 23.0 - 23.0 mm", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Unnotched", + "value": "0.0510", + "english": "0.0955 ft-lb/in", + "comments": "NB; Average value: 5.01 J/cm Grade Count:20", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm" + }, + { + "property_name": "Izod Impact, Notched (ISO)", + "value": "3.00 - 21000", + "english": "1.43 - 9990 ft-lb/in²", + "comments": "Average value: 13.4 kJ/m² Grade Count:192", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "kJ/m²" + }, + { + "property_name": "Izod Impact, Notched (ISO)", + "value": "3.00 - 50.0", + "english": "1.43 - 23.8 ft-lb/in²", + "comments": "Average value: 7.72 kJ/m² Grade Count:62", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - -20.0 °C", + "unit": "kJ/m²" + }, + { + "property_name": "Izod Impact, Unnotched (ISO)", + "value": "35.7 - 21000", + "english": "17.0 - 9990 ft-lb/in²", + "comments": "Average value: 79.2 kJ/m² Grade Count:29", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "kJ/m²" + }, + { + "property_name": "Izod Impact, Unnotched (ISO)", + "value": "55.0", + "english": "26.2 ft-lb/in²", + "comments": "NB; Average value: 88.8 kJ/m² Grade Count:12", + "section": "Mechanical Properties", + "test_condition": "@Temperature -30.0 - 30.0 °C", + "unit": "kJ/m²" + }, + { + "property_name": "Charpy Impact Unnotched", + "value": "1.00", + "english": "4.76 ft-lb/in²", + "comments": "NB; Average value: 7.03 J/cm² Grade Count:268", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm²" + }, + { + "property_name": "Charpy Impact Unnotched", + "value": "0.400", + "english": "1.90 ft-lb/in²", + "comments": "NB; Average value: 7.36 J/cm² Grade Count:139", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 5.00 °C", + "unit": "J/cm²" + }, + { + "property_name": "Charpy Impact, Notched", + "value": "0.100 - 2100", + "english": "0.476 - 9990 ft-lb/in²", + "comments": "Average value: 1.34 J/cm² Grade Count:292", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J/cm²" + }, + { + "property_name": "Charpy Impact, Notched", + "value": "0.100", + "english": "0.476 ft-lb/in²", + "comments": "NB; Average value: 0.827 J/cm² Grade Count:140", + "section": "Mechanical Properties", + "test_condition": "@Temperature -40.0 - 5.00 °C", + "unit": "J/cm²" + }, + { + "property_name": "Gardner Impact", + "value": "6.78 - 22.6", + "english": "5.00 - 16.7 ft-lb", + "comments": "Average value: 13.6 J Grade Count:3", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J" + }, + { + "property_name": "Puncture Energy", + "value": "13.1 - 50.0", + "english": "9.66 - 36.9 ft-lb", + "comments": "Average value: 27.7 J Grade Count:4", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "J" + }, + { + "property_name": "Puncture Energy", + "value": "5.10 - 60.0", + "english": "3.76 - 44.3 ft-lb", + "comments": "Average value: 20.4 J Grade Count:3", + "section": "Mechanical Properties", + "test_condition": "@Temperature -30.0 - 30.0 °C", + "unit": "J" + }, + { + "property_name": "Coefficient of Friction", + "value": "0.100 - 0.420", + "english": "0.100 - 0.420", + "comments": "Average value: 0.246 Grade Count:13", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Coefficient of Friction, Static", + "value": "0.172 - 0.200", + "english": "0.172 - 0.200", + "comments": "Average value: 0.184 Grade Count:3", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Tensile Creep Modulus, 1 hour", + "value": "1000 - 3400", + "english": "145000 - 493000 psi", + "comments": "Average value: 2760 MPa Grade Count:9", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Tensile Creep Modulus, 1000 hours", + "value": "450 - 980", + "english": "65300 - 142000 psi", + "comments": "Average value: 676 MPa Grade Count:10", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "K (wear) Factor", + "value": "24.2 - 403 x 10⁻⁸", + "english": "12.0 - 200 x 10⁻¹⁰ in²³-min/ft-lb-hr", + "comments": "Average value: 308 x 10⁻⁸ mm³/N-M Grade Count:4", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "mm³/N-M" + }, + { + "property_name": "Taber Abrasion, mg/1000 Cycles", + "value": "4.00 - 8.00", + "english": "4.00 - 8.00", + "comments": "Average value: 6.75 Grade Count:12", + "section": "Mechanical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Electrical Resistivity", + "value": "4000 - 1.00e+17", + "english": "4000 - 1.00e+17 ohm-cm", + "comments": "Average value: 1.21e+15 ohm-cm Grade Count:196", + "section": "Electrical Properties", + "test_condition": "", + "unit": "ohm-cm" + }, + { + "property_name": "Surface Resistance", + "value": "100 - 1.00e+15", + "english": "100 - 1.00e+15 ohm", + "comments": "Average value: 1.19e+14 ohm Grade Count:116", + "section": "Electrical Properties", + "test_condition": "", + "unit": "ohm" + }, + { + "property_name": "Dielectric Constant", + "value": "1.60 - 15.0", + "english": "1.60 - 15.0", + "comments": "Average value: 4.31 Grade Count:128", + "section": "Electrical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Dielectric Strength", + "value": "9.84 - 120", + "english": "250 - 3050 kV/in", + "comments": "Average value: 31.5 kV/mm Grade Count:153", + "section": "Electrical Properties", + "test_condition": "", + "unit": "kV/mm" + }, + { + "property_name": "Dissipation Factor", + "value": "0.000200 - 0.320", + "english": "0.000200 - 0.320", + "comments": "Average value: 0.0616 Grade Count:82", + "section": "Electrical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Dielectric Loss Index", + "value": "0.0150 - 0.200", + "english": "0.0150 - 0.200", + "comments": "Average value: 0.0989 Grade Count:11", + "section": "Electrical Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Arc Resistance", + "value": "60.0 - 240", + "english": "60.0 - 240 sec", + "comments": "Average value: 144 sec Grade Count:29", + "section": "Electrical Properties", + "test_condition": "", + "unit": "sec" + }, + { + "property_name": "Comparative Tracking Index", + "value": "350 - 650", + "english": "350 - 650 V", + "comments": "Average value: 587 V Grade Count:150", + "section": "Electrical Properties", + "test_condition": "", + "unit": "V" + }, + { + "property_name": "Hot Wire Ignition, HWI", + "value": "7.00 - 60.0", + "english": "7.00 - 60.0 sec", + "comments": "Average value: 22.2 sec Grade Count:18", + "section": "Electrical Properties", + "test_condition": "", + "unit": "sec" + }, + { + "property_name": "High Amp Arc Ignition, HAI", + "value": "60.0 - 120", + "english": "60.0 - 120 arcs", + "comments": "Average value: 117 arcs Grade Count:18", + "section": "Electrical Properties", + "test_condition": "", + "unit": "arcs" + }, + { + "property_name": "High Voltage Arc-Tracking Rate, HVTR", + "value": "0.000 - 50.0", + "english": "0.000 - 1.97 in/min", + "comments": "Average value: 6.92 mm/min Grade Count:17", + "section": "Electrical Properties", + "test_condition": "", + "unit": "mm/min" + }, + { + "property_name": "CTE, linear", + "value": "40.0 - 130", + "english": "22.2 - 72.2 µin/in-°F", + "comments": "Average value: 81.2 µm/m-°C Grade Count:107", + "section": "Thermal Properties", + "test_condition": "", + "unit": "µm/m-°C" + }, + { + "property_name": "CTE, linear", + "value": "71.0 - 100", + "english": "39.4 - 55.6 µin/in-°F", + "comments": "Average value: 95.7 µm/m-°C Grade Count:18", + "section": "Thermal Properties", + "test_condition": "@Temperature 35.0 - 55.0 °C", + "unit": "µm/m-°C" + }, + { + "property_name": "CTE, linear", + "value": "71.0 - 100", + "english": "39.4 - 55.6 µin/in-°F", + "comments": "Average value: 95.7 µm/m-°C Grade Count:48", + "section": "Thermal Properties", + "test_condition": "@Thickness 2.00 - 2.00 mm", + "unit": "µm/m-°C" + }, + { + "property_name": "CTE, linear, Transverse to Flow", + "value": "67.0 - 130", + "english": "37.2 - 72.2 µin/in-°F", + "comments": "Average value: 95.7 µm/m-°C Grade Count:48", + "section": "Thermal Properties", + "test_condition": "", + "unit": "µm/m-°C" + }, + { + "property_name": "Specific Heat Capacity", + "value": "1.45 - 2.10", + "english": "0.347 - 0.502 BTU/lb-°F", + "comments": "Average value: 1.72 J/g-°C Grade Count:29", + "section": "Thermal Properties", + "test_condition": "", + "unit": "J/g-°C" + }, + { + "property_name": "Thermal Conductivity", + "value": "0.200 - 11.0", + "english": "1.39 - 76.3 BTU-in/hr-ft²-°F", + "comments": "Average value: 1.06 W/m-K Grade Count:45", + "section": "Thermal Properties", + "test_condition": "", + "unit": "W/m-K" + }, + { + "property_name": "Melting Point", + "value": "197 - 270", + "english": "387 - 518 °F", + "comments": "Average value: 258 °C Grade Count:356", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Maximum Service Temperature, Air", + "value": "70.0 - 249", + "english": "158 - 480 °F", + "comments": "Average value: 136 °C Grade Count:60", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Hot Ball Pressure Test", + "value": "125 - 165", + "english": "257 - 329 °F", + "comments": "Average value: 158 °C Grade Count:5", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Deflection Temperature at 0.46 MPa (66 psi)", + "value": "70.0 - 260", + "english": "158 - 500 °F", + "comments": "Average value: 211 °C Grade Count:284", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Deflection Temperature at 1.8 MPa (264 psi)", + "value": "45.0 - 250", + "english": "113 - 482 °F", + "comments": "Average value: 91.6 °C Grade Count:395", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Deflection Temperature at 8.0 MPa", + "value": "50.0 - 60.0", + "english": "122 - 140 °F", + "comments": "Average value: 53.3 °C Grade Count:3", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Vicat Softening Point", + "value": "23.0 - 255", + "english": "73.4 - 491 °F", + "comments": "Average value: 233 °C Grade Count:96", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Minimum Service Temperature, Air", + "value": "-40.0 - -20.0", + "english": "-40.0 - -4.00 °F", + "comments": "Average value: -30.0 °C Grade Count:12", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Glass Transition Temp, Tg", + "value": "5.00 - 72.0", + "english": "41.0 - 162 °F", + "comments": "Average value: 39.1 °C Grade Count:10", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Decomposition Temperature", + "value": "300 - 310", + "english": "572 - 590 °F", + "comments": "Average value: 308 °C Grade Count:10", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "UL RTI, Electrical", + "value": "65.0 - 140", + "english": "149 - 284 °F", + "comments": "Average value: 124 °C Grade Count:23", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "UL RTI, Mechanical with Impact", + "value": "65.0 - 110", + "english": "149 - 230 °F", + "comments": "Average value: 77.8 °C Grade Count:23", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "UL RTI, Mechanical without Impact", + "value": "65.0 - 125", + "english": "149 - 257 °F", + "comments": "Average value: 86.2 °C Grade Count:23", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Flammability, UL94", + "value": "HB - V-0", + "english": "HB - V-0", + "comments": "Grade Count:234", + "section": "Thermal Properties", + "test_condition": "", + "unit": "" + }, + { + "property_name": "Flame Spread", + "value": "0.000 - 30.0", + "english": "0.000 - 1.18 in/min", + "comments": "Average value: 10.0 mm/min Grade Count:6", + "section": "Thermal Properties", + "test_condition": "", + "unit": "mm/min" + }, + { + "property_name": "Oxygen Index", + "value": "20.0 - 34.0", + "english": "20.0 - 34.0%", + "comments": "Average value: 26.1% Grade Count:48", + "section": "Thermal Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Glow Wire Test", + "value": "649 - 960", + "english": "1200 - 1760 °F", + "comments": "Average value: 837 °C Grade Count:58", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Ignition Temperature", + "value": "400", + "english": "752 °F", + "comments": "Average value: 400 °C Grade Count:10", + "section": "Thermal Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Yellow Index", + "value": "-10.0 - 8.00", + "english": "-10.0 - 8.00%", + "comments": "Average value: -4.00% Grade Count:14", + "section": "Optical Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Processing Temperature", + "value": "80.0 - 310", + "english": "176 - 590 °F", + "comments": "Average value: 258 °C Grade Count:68", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Nozzle Temperature", + "value": "250 - 310", + "english": "482 - 590 °F", + "comments": "Average value: 285 °C Grade Count:75", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Die Temperature", + "value": "270 - 295", + "english": "518 - 563 °F", + "comments": "Average value: 283 °C Grade Count:6", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Melt Temperature", + "value": "209 - 320", + "english": "409 - 608 °F", + "comments": "Average value: 279 °C Grade Count:250", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Mold Temperature", + "value": "20.0 - 150", + "english": "68.0 - 302 °F", + "comments": "Average value: 75.6 °C Grade Count:263", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Drying Temperature", + "value": "48.9 - 140", + "english": "120 - 284 °F", + "comments": "Average value: 81.5 °C Grade Count:223", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Moisture Content", + "value": "0.0200 - 0.500", + "english": "0.0200 - 0.500%", + "comments": "Average value: 0.158% Grade Count:73", + "section": "Processing Properties", + "test_condition": "", + "unit": "%" + }, + { + "property_name": "Dew Point", + "value": "-40.0 - -17.8", + "english": "-40.0 - 0.000 °F", + "comments": "Average value: -22.2 °C Grade Count:5", + "section": "Processing Properties", + "test_condition": "", + "unit": "°C" + }, + { + "property_name": "Injection Pressure", + "value": "3.45 - 138", + "english": "500 - 20000 psi", + "comments": "Average value: 79.7 MPa Grade Count:45", + "section": "Processing Properties", + "test_condition": "", + "unit": "MPa" + }, + { + "property_name": "Vent Depth", + "value": "0.000762", + "english": "0.000300 in", + "comments": "Average value: 0.000762 cm Grade Count:4", + "section": "Processing Properties", + "test_condition": "", + "unit": "cm" + } + ] + } +] \ No newline at end of file diff --git a/page_files/Categorized_Search.py b/page_files/Categorized_Search.py new file mode 100644 index 0000000000000000000000000000000000000000..3d36a67bed2b5e2949f27caebfaa6c5c60e85d03 --- /dev/null +++ b/page_files/Categorized_Search.py @@ -0,0 +1,900 @@ +import base64 +import re +from pathlib import Path + +import pandas as pd +import streamlit as st +from PIL import Image + +from data_loader import get_all_sections, load_material_data + +st.markdown( + """ + + """, + unsafe_allow_html=True, +) + + +@st.cache_data +def load_data(material_type: str) -> pd.DataFrame: + return load_material_data(material_type) + + +@st.cache_data +def load_all_data() -> pd.DataFrame: + frames = [] + for material_type in ["Composites", "Polymers", "Fibers"]: + frame = load_data(material_type) + frame["_class"] = material_type + frames.append(frame) + return pd.concat(frames, ignore_index=True) + + +def extract_matrix_fiber(abbr: str): + if not isinstance(abbr, str): + return None, None + + text = abbr.lower() + + matrix_map = { + "epoxy": "Epoxy", + "cyanate ester": "Cyanate Ester", + "cynate ester": "Cyanate Ester", + "polypropylene": "Polypropylene", + "pp": "Polypropylene", + "peek": "PEEK", + "pei": "PEI", + "nylon": "Nylon", + "pa6": "PA6", + "polyester": "Polyester", + "vinyl ester": "Vinyl Ester", + "phenolic": "Phenolic", + } + + fiber_map = { + "carbon": "Carbon Fiber", + "glass": "Glass Fiber", + "e-glass": "E-Glass Fiber", + "s-glass": "S-Glass Fiber", + "aramid": "Aramid Fiber", + "kevlar": "Kevlar Fiber", + "basalt": "Basalt Fiber", + "natural": "Natural Fiber", + } + + matrix = next((value for key, value in matrix_map.items() if key in text), None) + fiber = next((value for key, value in fiber_map.items() if key in text), None) + return matrix, fiber + + +def toggle_class(material_class: str): + active = list(st.session_state.active_classes) + if material_class in active: + active.remove(material_class) + else: + active.append(material_class) + + order = ["Composites", "Polymers", "Fibers"] + st.session_state.active_classes = [item for item in order if item in active] + st.session_state.current_page = 0 + + +def visible_page_numbers(current_page: int, total_pages: int): + if total_pages <= 6: + return list(range(total_pages)) + + pages = {0, 1, 2, current_page, total_pages - 1} + if current_page - 1 > 2: + pages.add(current_page - 1) + if current_page + 1 < total_pages - 1: + pages.add(current_page + 1) + return sorted(pages) + + +defaults = { + "active_classes": [], + "selected_props": [], + "selected_matrix": "All", + "selected_fiber": "All", + "selected_row": None, + "current_page": 0, + "_search_term": None, + "top_search_input": "", + "inspect_section": None, + "inspect_property": None, + "_reset_prop_checks": False, +} + +for key, value in defaults.items(): + if key not in st.session_state: + st.session_state[key] = value + +if "material_type" in st.session_state: + incoming_type = st.session_state.pop("material_type") + if incoming_type in ["Composites", "Polymers", "Fibers"]: + st.session_state.active_classes = [incoming_type] + +if "selected_section" in st.session_state: + st.session_state.selected_props = [st.session_state.pop("selected_section")] + st.session_state._reset_prop_checks = True + +if "search_term" in st.session_state: + st.session_state._search_term = st.session_state.pop("search_term") + +if st.session_state._search_term and not st.session_state.top_search_input: + st.session_state.top_search_input = st.session_state._search_term + +all_data = load_all_data() +if "user_uploaded_data" in st.session_state: + uploaded = st.session_state["user_uploaded_data"].copy() + uploaded["_class"] = uploaded["material_class"].map( + {"Polymer": "Polymers", "Fiber": "Fibers", "Composite": "Composites"} + ) + all_data = pd.concat([all_data, uploaded], ignore_index=True) + +st.session_state["base_data"] = all_data + +meta = ( + all_data[["material_abbreviation", "material_name", "_class"]] + .fillna("") + .drop_duplicates(subset=["material_abbreviation"]) + .reset_index(drop=True) +) +meta[["Matrix", "Fiber"]] = meta["material_abbreviation"].apply( + lambda value: pd.Series(extract_matrix_fiber(value)) +) + +all_sections = get_all_sections() + +if st.session_state._reset_prop_checks: + selected = set(st.session_state.selected_props) + for index, section in enumerate(all_sections): + st.session_state[f"prop_check_{index}"] = section in selected + st.session_state._reset_prop_checks = False + +filtered_meta = meta.copy() +if st.session_state.active_classes: + filtered_meta = filtered_meta[filtered_meta["_class"].isin(st.session_state.active_classes)] +if st.session_state.selected_matrix != "All": + filtered_meta = filtered_meta[filtered_meta["Matrix"] == st.session_state.selected_matrix] +if st.session_state.selected_fiber != "All": + filtered_meta = filtered_meta[filtered_meta["Fiber"] == st.session_state.selected_fiber] + +if st.session_state._search_term: + term = st.session_state._search_term + try: + pattern = re.compile(term, re.IGNORECASE) + except re.error: + pattern = re.compile(re.escape(term), re.IGNORECASE) + + filtered_meta = filtered_meta[ + filtered_meta["material_abbreviation"].astype(str).str.contains(pattern) + | filtered_meta["material_name"].astype(str).str.contains(pattern) + ] + +if st.session_state.selected_props: + valid_abbr = all_data[ + all_data["section"].isin(st.session_state.selected_props) & all_data["value"].notna() + ]["material_abbreviation"].unique() + filtered_meta = filtered_meta[filtered_meta["material_abbreviation"].isin(valid_abbr)] + +filtered_meta = filtered_meta.reset_index(drop=True) + +PAGE_SIZE = 5 +total = len(filtered_meta) +total_pages = max(1, (total + PAGE_SIZE - 1) // PAGE_SIZE) +st.session_state.current_page = min(st.session_state.current_page, total_pages - 1) + +start = st.session_state.current_page * PAGE_SIZE +end = start + PAGE_SIZE +page_meta = filtered_meta.iloc[start:end].reset_index(drop=True) + +left_col, right_col = st.columns([1.03, 3.07], gap="small") + +with left_col: + with st.container(border=True): + logo_html = "" + logo_path = Path("logo.png") + if logo_path.exists(): + with logo_path.open("rb") as file_handle: + logo_b64 = base64.b64encode(file_handle.read()).decode() + logo_html = ( + f"" + ) + + st.markdown( + f"", + unsafe_allow_html=True, + ) + + st.markdown("
🧩 Material Class
", unsafe_allow_html=True) + + cls_a, cls_b = st.columns(2) + with cls_a: + if st.button( + "Composites", + key="class_comp", + use_container_width=True, + type="primary" if "Composites" in st.session_state.active_classes else "secondary", + ): + toggle_class("Composites") + st.rerun() + + with cls_b: + if st.button( + "Polymers", + key="class_poly", + use_container_width=True, + type="primary" if "Polymers" in st.session_state.active_classes else "secondary", + ): + toggle_class("Polymers") + st.rerun() + + if st.button( + "Fibers", + key="class_fib", + use_container_width=True, + type="primary" if "Fibers" in st.session_state.active_classes else "secondary", + ): + toggle_class("Fibers") + st.rerun() + + st.markdown("
🧪 Composition
", unsafe_allow_html=True) + composite_meta = meta[meta["_class"] == "Composites"] + matrix_options = ["All"] + sorted([item for item in composite_meta["Matrix"].dropna().unique() if item]) + fiber_options = ["All"] + sorted([item for item in composite_meta["Fiber"].dropna().unique() if item]) + + st.markdown("
🧱 Matrix
", unsafe_allow_html=True) + matrix_value = st.selectbox( + "Matrix", + matrix_options, + index=matrix_options.index(st.session_state.selected_matrix) + if st.session_state.selected_matrix in matrix_options + else 0, + key="matrix_select", + label_visibility="collapsed", + ) + + st.markdown("
🧵 Fiber
", unsafe_allow_html=True) + fiber_value = st.selectbox( + "Fiber", + fiber_options, + index=fiber_options.index(st.session_state.selected_fiber) + if st.session_state.selected_fiber in fiber_options + else 0, + key="fiber_select", + label_visibility="collapsed", + ) + + if matrix_value != st.session_state.selected_matrix: + st.session_state.selected_matrix = matrix_value + st.session_state.current_page = 0 + st.rerun() + + if fiber_value != st.session_state.selected_fiber: + st.session_state.selected_fiber = fiber_value + st.session_state.current_page = 0 + st.rerun() + + st.markdown("
📋 Property Types
", unsafe_allow_html=True) + selected_props = [] + with st.container(height=480): + for index, section in enumerate(all_sections): + key = f"prop_check_{index}" + if key not in st.session_state: + st.session_state[key] = section in st.session_state.selected_props + + if st.checkbox(section, key=key): + selected_props.append(section) + + if selected_props != st.session_state.selected_props: + st.session_state.selected_props = selected_props + st.session_state.current_page = 0 + st.rerun() + + if st.button( + "🔎 Inspect", + key="inspect_btn", + use_container_width=True, + type="primary", + disabled=st.session_state.selected_row is None, + ): + st.info("Open the Inspect tab on the right panel.") + + if st.session_state.selected_row: + selected_abbr, selected_name = st.session_state.selected_row + st.markdown( + f"
Selected
{selected_name}
{selected_abbr}
", + unsafe_allow_html=True, + ) + +with right_col: + with st.container(border=True): + with st.container(key="top_search_row"): + input_col, btn_col = st.columns([0.82, 0.18], gap="small") + with input_col: + search_query = st.text_input( + label="Search", + placeholder="Search by material name, property, or abbreviation...", + label_visibility="collapsed", + key="top_search_input", + ) + with btn_col: + search_clicked = st.button("Search", key="top_search_btn", use_container_width=True) + + if search_clicked: + query = (search_query or "").strip() + st.session_state._search_term = query if query else None + st.session_state.current_page = 0 + st.rerun() + + with st.container(border=True): + st.markdown( + """ +
+
INVENTORY / MATERIALS DATABASE
+
Materials Database
+
+ """, + unsafe_allow_html=True, + ) + + tab_materials, tab_dashboard, tab_inspect = st.tabs( + ["All Materials", "Dashboard", "Inspect"] + ) + + with tab_materials: + filter_label = ( + ", ".join(st.session_state.active_classes) + if st.session_state.active_classes + else "All Materials" + ) + shown_start = start + 1 if total > 0 else 0 + shown_end = min(end, total) + + row_left, row_right = st.columns([2.2, 1.2]) + with row_left: + st.markdown(f"
{filter_label}
", unsafe_allow_html=True) + with row_right: + st.markdown( + f"
Showing {shown_start}-{shown_end} of {total} materials
", + unsafe_allow_html=True, + ) + + selected_abbr = st.session_state.selected_row[0] if st.session_state.selected_row else None + + class_map = { + "Composites": "🔵 COMPOSITE", + "Polymers": "🟢 POLYMER", + "Fibers": "🟠 FIBER", + } + + if not page_meta.empty: + table_df = page_meta.copy() + table_df["Select"] = table_df["material_abbreviation"].eq(selected_abbr) + table_df["Class"] = table_df["_class"].map(class_map) + table_df["Actions"] = "" + table_df = table_df[ + ["Select", "material_name", "material_abbreviation", "Class", "Actions"] + ].rename( + columns={ + "material_name": "Material Name", + "material_abbreviation": "Abbreviation", + } + ) + else: + table_df = pd.DataFrame( + columns=["Select", "Material Name", "Abbreviation", "Class", "Actions"] + ) + + for _ in range(2): + table_df.loc[len(table_df)] = [False, "", "", "", ""] + + editor_df = st.data_editor( + table_df, + key=f"materials_editor_{st.session_state.current_page}", + use_container_width=True, + hide_index=True, + height=372, + row_height=46, + column_order=["Select", "Material Name", "Abbreviation", "Class", "Actions"], + column_config={ + "Select": st.column_config.CheckboxColumn("", width="small"), + "Material Name": st.column_config.TextColumn("MATERIAL NAME", width="large"), + "Abbreviation": st.column_config.TextColumn("ABBREVIATION", width="medium"), + "Class": st.column_config.TextColumn("CLASS", width="small"), + "Actions": st.column_config.TextColumn("ACTIONS", width="small"), + }, + disabled=["Material Name", "Abbreviation", "Class", "Actions"], + ) + + checked_rows = editor_df[ + editor_df["Select"] + & editor_df["Abbreviation"].astype(str).str.strip().ne("") + ] + if not checked_rows.empty: + chosen = checked_rows.iloc[0] + abbr = chosen["Abbreviation"] + name = chosen["Material Name"] + if ( + st.session_state.selected_row is None + or st.session_state.selected_row[0] != abbr + ): + st.session_state.selected_row = (abbr, name) + st.rerun() + info_col, nav_col = st.columns([2.4, 2.0]) + with info_col: + st.markdown( + f"
Showing {shown_start}-{shown_end} of {total} materials | Items per page: {PAGE_SIZE}
", + unsafe_allow_html=True, + ) + + with nav_col: + nav_items = [] + nav_items.append(("<<", 0, st.session_state.current_page == 0, False)) + nav_items.append(("<", max(0, st.session_state.current_page - 1), st.session_state.current_page == 0, False)) + + visible_pages = visible_page_numbers(st.session_state.current_page, total_pages) + previous = -1 + for number in visible_pages: + if previous >= 0 and number - previous > 1: + nav_items.append(("...", None, True, False)) + nav_items.append( + ( + str(number + 1), + number, + False, + number == st.session_state.current_page, + ) + ) + previous = number + + nav_items.append( + ( + ">", + min(total_pages - 1, st.session_state.current_page + 1), + st.session_state.current_page >= total_pages - 1, + False, + ) + ) + nav_items.append( + ( + ">>", + total_pages - 1, + st.session_state.current_page >= total_pages - 1, + False, + ) + ) + + nav_columns = st.columns(len(nav_items)) + for idx, (column, item) in enumerate(zip(nav_columns, nav_items)): + label, target_page, disabled, active = item + with column: + if label == "...": + st.markdown("
...
", unsafe_allow_html=True) + else: + if st.button( + label, + key=f"page_btn_{idx}_{label}_{target_page}", + use_container_width=True, + disabled=disabled, + type="primary" if active else "secondary", + ): + st.session_state.current_page = target_page + st.rerun() + + with tab_dashboard: + st.markdown( + "
Dashboard coming soon. Analytics and visualizations will appear here.
", + unsafe_allow_html=True, + ) + + with tab_inspect: + if not st.session_state.selected_row: + st.warning("Select a material in All Materials first.") + else: + selected_abbr, selected_name = st.session_state.selected_row + st.markdown(f"**Material:** {selected_name}") + st.caption(selected_abbr) + + material_df = all_data[ + (all_data["material_abbreviation"] == selected_abbr) + & (all_data["value"].notna()) + & (all_data["property_name"].notna()) + ] + + section_options = sorted(material_df["section"].dropna().unique().tolist()) + if not section_options: + st.warning("No property data found for this material.") + else: + if st.session_state.inspect_section not in section_options: + st.session_state.inspect_section = section_options[0] + + section_choice = st.selectbox( + "Type of Property", + section_options, + index=section_options.index(st.session_state.inspect_section), + key="inspect_section_select", + ) + st.session_state.inspect_section = section_choice + + properties_df = ( + material_df[material_df["section"] == section_choice][ + ["property_name", "section"] + ] + .drop_duplicates() + .reset_index(drop=True) + ) + st.dataframe(properties_df, use_container_width=True, hide_index=True, height=240) + + property_options = properties_df["property_name"].dropna().tolist() + if property_options: + if st.session_state.inspect_property not in property_options: + st.session_state.inspect_property = property_options[0] + + property_choice = st.selectbox( + "Property", + property_options, + index=property_options.index(st.session_state.inspect_property), + key="inspect_property_select", + ) + st.session_state.inspect_property = property_choice + + if st.button("Search", key="inspect_search", type="primary"): + result = all_data[ + (all_data["material_abbreviation"] == selected_abbr) + & (all_data["property_name"] == property_choice) + & (all_data["value"].notna()) + ] + + if result.empty: + st.warning("No data found for this material-property combination") + else: + st.subheader("Property Data") + st.dataframe(result.T, use_container_width=True) + + st.subheader("Property Graph") + image_path = Path("images") / f"{selected_abbr}_{property_choice}.png" + if image_path.exists(): + image = Image.open(image_path) + st.image(image, use_container_width=True, caption="Stress strain curve") + else: + st.caption("No plot image available for this material-property pair.") + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page_files/Contact_Team.py b/page_files/Contact_Team.py new file mode 100644 index 0000000000000000000000000000000000000000..9db93bf27372c98126eeff488813acf70a5b5b57 --- /dev/null +++ b/page_files/Contact_Team.py @@ -0,0 +1,40 @@ +import streamlit as st +from pathlib import Path +from PIL import Image, ImageOps + +IMG_DIR = Path("images") +TARGET_SIZE = (213, 310) + +def fixed_image(name): + img = Image.open(IMG_DIR / name).convert("RGB") + return ImageOps.fit(img, TARGET_SIZE, Image.LANCZOS, centering=(0.5, 0.5)) + +c1, c2, c3 = st.columns([1, 2, 1]) +with c2: + st.subheader("Team Members") + +col1, col2, col3 = st.columns(3) +with col1: + st.image(fixed_image("GangLi.jpg")) + st.markdown("**Gang Li** \nProfessor of Mechanical Engineering, Clemson University \ngli@clemson.edu") + +with col2: + st.image(fixed_image("Mathias.jpg")) + st.markdown("**Heider, Mathias** \nResearch Assistant - CSE PhD Student \nUniversity of Delaware \nmheider@udel.edu") + +with col3: + st.image(fixed_image("Abhijit.jpg")) + st.markdown("**Abhijit Varanasi** \nLab Specialist - Clemson University \nMFA Graduate, Clemson University \nBE - CSE \navarana@clemson.edu") + +st.write("") + +sp1, col4, sp2, col5, sp3 = st.columns([1, 3, 1, 3, 1]) +with col4: + st.image(fixed_image("Tejaswi.jpg")) + st.markdown("**Tejaswi Gudimetla** \nLab Aide - Clemson University \nvgudime@clemson.edu") + +with col5: + st.image(fixed_image("Pradeep.jpg")) + st.markdown("**Sai Aditya Pradeep** \nResearch and Development Engineer, University of Delaware \nspradeep@udel.edu") + +st.sidebar.image("logo.png", caption=" ", width=150) diff --git a/page_files/Home.py b/page_files/Home.py new file mode 100644 index 0000000000000000000000000000000000000000..9cace083398a06951c11f488cb370fc4c3e6145f --- /dev/null +++ b/page_files/Home.py @@ -0,0 +1,722 @@ +import streamlit as st +import streamlit.components.v1 as components +from pathlib import Path +import base64 +from streamlit_card import card +from data_loader import get_all_sections +import random +from data_loader import get_all_sections +import re + + +ALL_CARDS = [ + ("Composites", "Material class", "material", "Composites"), + ("Polymers", "Material class", "material", "Polymers"), + ("Fibers", "Material class", "material", "Fibers"), +] + +sections = get_all_sections() +for section in sections: + ALL_CARDS.append((section, "Property type", "section", section)) + +if "visible_cards" not in st.session_state: + random.shuffle(ALL_CARDS) + st.session_state.visible_cards = ALL_CARDS[:4] + +VISIBLE_CARDS = st.session_state.visible_cards +prop_count = len([c for c in ALL_CARDS if c[2] == "section"]) + +def get_card_icon(title: str, card_type: str) -> str: + if card_type == "material": + icons = {"composites": "🧱", "polymers": "🔬", "fibers": "🧵"} + return icons.get(title.lower(), "🧱") + t = title.lower() + if "mechanical" in t: return "⚙️" + if "thermal" in t: return "🔥" + if "electrical" in t: return "⚡" + if "physical" in t: return "⚖️" + if "processing" in t: return "🔧" + if "optical" in t: return "🔭" + if "chemical" in t: return "🧪" + if "flammab" in t: return "🔴" + if "component" in t: return "🧩" + if "descriptive" in t: return "📋" + return "📋" + +def img_to_b64(path): + try: + ext = Path(path).suffix.lower().replace(".", "") + mime = "png" if ext == "png" else "jpeg" + with open(path, "rb") as f: + data = base64.b64encode(f.read()).decode() + return f"data:image/{mime};base64,{data}" + except Exception: + return "" + + +home_img = img_to_b64("images/Home.png") +logo_img = img_to_b64("logo.png") + +st.markdown(""" + + """, + unsafe_allow_html=True + ) + +# Global style overrides +st.markdown(""" + +""", unsafe_allow_html=True) + + +# Helper +def go_categorized(material=None, search=None): + if material: + st.session_state.material_type = material + if search: + st.session_state.search_term = search + st.switch_page("page_files/Categorized_Search.py") + + +def go_upload(): + st.switch_page("page_files/Upload_Data.py") + + +# ══════════════════════════════════════════════════════════════════════════════ +# 1. ANIMATION SECTION (pure HTML, no clickables needed) +# ══════════════════════════════════════════════════════════════════════════════ +about_img_html = ( + f"
AIM platform diagram
" + if home_img else + "
[ Platform diagram ]
" +) + +logo_html = ( + f"AIM Logo" + if logo_img else "" +) + +components.html(f""" + + + + + + + +
+

From Experiment to Database

+
+ +
+
Research
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ +
+
Database
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
Collect measurements
+
Process & validate
+
Stored in AIM
+
+
+ + + +""", height=700, scrolling=False) + +# ══════════════════════════════════════════════════════════════════════════════ +# 2. HERO , heading + description (static HTML) +# ══════════════════════════════════════════════════════════════════════════════ +st.markdown(""" + +
+

Accelerate Your Composites Research

+

Access a centralized, open-source database for experimental composite material properties. + Polymer, fiber, and composite datasets , all in one place.

+
+""", unsafe_allow_html=True) + + + +# ══════════════════════════════════════════════════════════════════════════════ +# 4. STATS (static HTML) +# ══════════════════════════════════════════════════════════════════════════════ +st.markdown(f""" + +
+
3
Material Classes
+
{prop_count}
Properties Tracked
+
8
Research Teams
+
2
Universities
+
+""", unsafe_allow_html=True) + + +# ══════════════════════════════════════════════════════════════════════════════ +# 5. MAJOR CATEGORIES , Streamlit columns + containers + buttons +# ══════════════════════════════════════════════════════════════════════════════ +st.markdown(""" + +""", unsafe_allow_html=True) + + + +st.markdown("
", unsafe_allow_html=True) + +st.markdown(""" +
+

Quick Links

+

Open the pages you need most.

+
+ """, unsafe_allow_html=True) + +quick_cards = [ + ("Extract Data", "Platform", "page_files/Upload_Data.py", "about", "📋"), + ("Contact Team", "Support", "page_files/Contact_Team.py", "contact", " 🛡️"), + ("Search", "Data Page", "page_files/Categorized_Search.py", "search", "🔎"), +] + +cols = st.columns(3, gap="large") +for col, (title, tag, target_page, key_suffix, badge) in zip(cols, quick_cards): + with col: + if st.button( + f"{badge}\n\n{title}\n\n{tag}", + key=f"quick_{key_suffix}_page", + use_container_width=True, + ): + st.switch_page(target_page) + +st.markdown("
", unsafe_allow_html=True) + + + + +# ══════════════════════════════════════════════════════════════════════════════ +# 6. ABOUT + FOOTER (static HTML) +# ══════════════════════════════════════════════════════════════════════════════ +components.html(f""" + + + + + + +
+

About the Platform

+

Artificially Intelligent Manufacturing Paradigm (AIM) for Composites

+
+
+

The AIM Database tool serves as a powerful, centralized hub designed to streamline + collaboration and information exchange within the composite materials research community. + The platform enables researchers to contribute to a shared knowledge base by uploading + experimental datasets through secure terminals.

+

Users can submit specific measurements regarding mechanical properties, thermal behavior, + and rheology, alongside their published journal papers , ensuring that both raw data and + peer-reviewed findings are integrated into one cohesive system.

+

All contributed information is securely aggregated within a central cloud architecture, + allowing for efficient storage, organization, and retrieval across polymer, fiber, and + composite categories.

+
+
{about_img_html}
+
+
+ + + + +""", height=550, scrolling=False) + +st.markdown(""" + +""", unsafe_allow_html=True) + +brand_col, db_col, sup_col, _ = st.columns([4, 2, 2, 2], vertical_alignment="top", width="stretch") + +with brand_col: + st.markdown(f""" + + + """, unsafe_allow_html=True) + +with sup_col: + st.markdown('', unsafe_allow_html=True) + st.page_link( + "page_files/Categorized_Search.py", + label="Browse Materials", + ) + st.page_link( + "page_files/Categorized_Search.py", + label="Categorized Search", + ) + st.page_link( + "page_files/Upload_Data.py", + label="Upload Data", + ) + +with _: + st.markdown('', unsafe_allow_html=True) + st.page_link( + "page_files/Contact_Team.py", + label="Contact Team", + ) + + + diff --git a/page_files/Upload_Data.py b/page_files/Upload_Data.py new file mode 100644 index 0000000000000000000000000000000000000000..254cb8f27b1fe300e6fea6648630daf4d0321d1f --- /dev/null +++ b/page_files/Upload_Data.py @@ -0,0 +1,887 @@ +import os +import json +import tempfile +import base64 + +import fitz # PyMuPDF +import pandas as pd +import streamlit as st + +from data_loader import insert_material_rows +from page_files.categorized.Backend.upload_backend import ( + call_gemini_from_bytes, + convert_to_dataframe, + create_zip, + extract_images, + save_matched_images, + save_single_image_with_property, +) +def inject_upload_page_styles(): + st.markdown( + """ + + """, + unsafe_allow_html=True, + ) + + +def render_top_bar(): + logo_html = "" + try: + with open("logo.png", "rb") as fh: + logo_b64 = base64.b64encode(fh.read()).decode() + logo_html = f"AIM" + except Exception: + logo_html = "" + + st.markdown( + f"
{logo_html}AIM Composites
", + unsafe_allow_html=True, + ) + + +def input_form(): + property_categories = { + "Polymer": [ + "Thermal", + "Mechanical", + "Processing", + "Physical", + "Descriptive", + ], + "Fiber": [ + "Mechanical", + "Physical", + "Thermal", + "Descriptive", + ], + "Composite": [ + "Mechanical", + "Thermal", + "Processing", + "Physical", + "Descriptive", + "Composition / Reinforcement", + "Architecture / Structure", + ], + } + + property_names = { + "Polymer": { + "Thermal": [ + "Glass transition temperature (Tg)", + "Melting temperature (Tm)", + "Crystallization temperature (Tc)", + "Degree of crystallinity", + "Decomposition temperature", + ], + "Mechanical": [ + "Tensile modulus", + "Tensile strength", + "Elongation at break", + "Flexural modulus", + "Impact strength", + ], + "Processing": [ + "Melt flow index (MFI)", + "Processing temperature", + "Cooling rate", + "Mold shrinkage", + ], + "Physical": [ + "Density", + "Specific gravity", + ], + "Descriptive": [ + "Material grade", + "Manufacturer", + ], + }, + "Fiber": { + "Mechanical": [ + "Tensile modulus", + "Tensile strength", + "Strain to failure", + ], + "Physical": [ + "Density", + "Fiber diameter", + ], + "Thermal": [ + "Decomposition temperature", + ], + "Descriptive": [ + "Fiber type", + "Surface treatment", + ], + }, + "Composite": { + "Mechanical": [ + "Longitudinal modulus (E1)", + "Transverse modulus (E2)", + "Shear modulus (G12)", + "Poissons ratio (V12)", + "Tensile strength (fiber direction)", + "Interlaminar shear strength", + ], + "Thermal": [ + "Glass transition temperature (matrix)", + "Coefficient of thermal expansion (CTE)", + ], + "Processing": [ + "Curing temperature", + "Curing pressure", + ], + "Physical": [ + "Density", + ], + "Descriptive": [ + "Laminate type", + ], + "Composition / Reinforcement": [ + "Fiber volume fraction", + "Fiber weight fraction", + "Fiber type", + "Matrix type", + ], + "Architecture / Structure": [ + "Weave type", + "Ply orientation", + "Number of plies", + "Stacking sequence", + ], + }, + } + + with st.container(border=False, key="material_ident_card"): + st.markdown("
iMaterial Identification
", unsafe_allow_html=True) + + col_a, col_b = st.columns(2) + with col_a: + material_class = st.selectbox( + "Material Class", + ("Polymer", "Fiber", "Composite"), + index=None, + placeholder="Choose material class", + key="manual_material_class", + ) + with col_b: + if material_class: + property_category = st.selectbox( + "Property Type", + property_categories[material_class], + index=None, + placeholder="Choose property type", + key="manual_property_category", + ) + else: + property_category = None + st.selectbox( + "Property Type", + ["Choose material class first"], + index=0, + disabled=True, + key="manual_property_category_disabled", + ) + + if material_class and property_category: + property_options = property_names[material_class][property_category] + ["Something else"] + property_name = st.selectbox( + "Property Name", + property_options, + index=None, + placeholder="Choose property", + key="manual_property_name", + ) + else: + property_name = None + + custom_property_name = "" + if property_name == "Something else": + custom_property_name = st.text_input( + "Custom Property Name", + placeholder="Type property name", + key="manual_custom_property_name", + ).strip() + + selected_property_name = ( + custom_property_name if property_name == "Something else" else property_name + ) + + if material_class and property_category and selected_property_name: + with st.container(border=False, key="material_form_card"): + with st.form("user_input"): + st.subheader("Enter Data") + + material_name = st.text_input("Material Name") + material_abbr = st.text_input("Material Abbreviation") + + value = st.text_input("Value") + unit = st.text_input("Unit (SI)") + english = st.text_input("English Units") + test_condition = st.text_input("Test Condition") + comments = st.text_area("Comments") + + submitted = st.form_submit_button("Submit") + + if submitted: + if not (material_name and value): + st.error("Material name and value are required.") + return False + else: + input_db = pd.DataFrame( + [ + { + "material_class": material_class, + "material_name": material_name, + "material_abbreviation": material_abbr, + "section": property_category, + "property_name": selected_property_name, + "value": value, + "unit": unit, + "english": english, + "test_condition": test_condition, + "comments": comments, + } + ] + ) + + try: + inserted = insert_material_rows(input_db) + except Exception as exc: + st.error(f"Failed to save to PostgreSQL: {exc}") + return False + + if inserted <= 0: + st.error("No rows were inserted into PostgreSQL.") + return False + + st.cache_data.clear() + st.success("Property added successfully to PostgreSQL.") + st.dataframe(input_db) + return True + + return False + + return False + + +def main(): + inject_upload_page_styles() + render_top_bar() + + + st.subheader("Submit Scientific Material") + st.caption("Provide technical data and research documentation for the central repository.") + + + if "image_results" not in st.session_state: + st.session_state.image_results = [] + if "pdf_processed" not in st.session_state: + st.session_state.pdf_processed = False + if "current_pdf_name" not in st.session_state: + st.session_state.current_pdf_name = None + if "form_submitted" not in st.session_state: + st.session_state.form_submitted = False + if "pdf_data_extracted" not in st.session_state: + st.session_state.pdf_data_extracted = False + if "pdf_extracted_df" not in st.session_state: + st.session_state.pdf_extracted_df = pd.DataFrame() + if "saved_image_mapping" not in st.session_state: + st.session_state.saved_image_mapping = {} + + + with st.container(border=True, key="ud_main_card"): + if input_form(): + st.session_state.form_submitted = True + + + st.markdown("
iResearch Documentation
", unsafe_allow_html=True) + + uploaded_file = st.file_uploader( + "Upload PDF (Material Datasheet or Research Paper)", type=["pdf"] + ) + + + if not uploaded_file: + st.info("Upload a PDF to extract material data and plots") + + if not uploaded_file: + st.session_state.pdf_processed = False + st.session_state.current_pdf_name = None + st.session_state.image_results = [] + st.session_state.form_submitted = False + st.session_state.pdf_data_extracted = False + st.session_state.pdf_extracted_df = pd.DataFrame() + st.session_state.saved_image_mapping = {} + return + + paper_id = os.path.splitext(uploaded_file.name)[0].replace(" ", "_") + + if st.session_state.current_pdf_name != uploaded_file.name: + st.session_state.pdf_processed = False + st.session_state.current_pdf_name = uploaded_file.name + st.session_state.image_results = [] + st.session_state.form_submitted = False + st.session_state.saved_image_mapping = {} + + if st.session_state.form_submitted: + st.session_state.form_submitted = False + st.info( + "A Form was submitted. But your previous extracted data has been added already. " + "If you want to extract more data/plots upload again" + ) + tab1, tab2 = st.tabs(["Material Data", "Extracted Plots"]) + with tab1: + st.info("Material data from form has been added to database.") + with tab2: + st.info("Plots already extracted") + return + + tab1, tab2 = st.tabs([" Material Data", " Extracted Plots"]) + + with tempfile.TemporaryDirectory() as tmpdir: + pdf_path = os.path.join(tmpdir, uploaded_file.name) + with open(pdf_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + with tab1: + st.subheader("Material Properties Data") + + if not st.session_state.pdf_data_extracted: + with st.spinner(" Extracting material data..."): + with open(pdf_path, "rb") as f: + pdf_bytes = f.read() + + data = call_gemini_from_bytes(pdf_bytes, uploaded_file.name) + + if data: + df = convert_to_dataframe(data) + if not df.empty: + st.session_state.pdf_extracted_df = df + st.session_state.pdf_data_extracted = True + st.session_state.pdf_extracted_meta = data + else: + st.warning("No data extracted") + else: + st.error("Failed to extract data from PDF") + + df = st.session_state.pdf_extracted_df + + if not df.empty: + data = st.session_state.get("pdf_extracted_meta", {}) + st.success(f"Extracted {len(df)} properties") + + col1, col2 = st.columns(2) + with col1: + st.metric("Material", data.get("material_name", "N/A")) + with col2: + st.metric("Abbreviation", data.get("material_abbreviation", "N/A")) + + st.dataframe(df, use_container_width=True, height=400) + st.subheader("Assign Material Category") + + extracted_material_class = st.selectbox( + "Select category for this material", + ["Polymer", "Fiber", "Composite"], + index=None, + placeholder="Required before adding to database", + ) + + if st.button("+Add to Database"): + if not extracted_material_class: + st.error("Please select a material category before adding.") + else: + df["material_class"] = extracted_material_class + df["material_type"] = extracted_material_class + + if st.session_state.image_results: + with st.spinner("Saving matched plot images..."): + saved_images = save_matched_images( + df, + st.session_state.image_results, + save_dir="images", + ) + + if saved_images: + st.success(f" Saved {len(saved_images)} plot image(s)") + with st.expander("View saved images"): + for img_info in saved_images: + st.write( + f"? **{img_info['property']}** ? {img_info['caption']}" + ) + st.write(f" Saved to: `{img_info['path']}`") + else: + st.info("? No plots matched the extracted properties") + + if "user_uploaded_data" not in st.session_state: + st.session_state["user_uploaded_data"] = df + else: + st.session_state["user_uploaded_data"] = pd.concat( + [st.session_state["user_uploaded_data"], df], + ignore_index=True, + ) + + st.success(f"Added to {extracted_material_class} database!") + + with tab2: + st.subheader("Extracted Plot Images") + + if not st.session_state.pdf_processed: + with st.spinner(" Extracting plots from PDF..."): + doc = fitz.open(pdf_path) + st.session_state.image_results = extract_images(doc) + doc.close() + st.session_state.pdf_processed = True + + if st.session_state.image_results: + has_extracted_data = not st.session_state.pdf_extracted_df.empty + + if has_extracted_data: + mat_abbr = st.session_state.pdf_extracted_df.iloc[0][ + "material_abbreviation" + ] + property_list = ( + st.session_state.pdf_extracted_df["property_name"].unique().tolist() + ) + + st.info( + f" Material: **{mat_abbr}** | {len(property_list)} properties available for mapping" + ) + else: + st.warning( + " No extracted material data found. Please extract material data first (Tab 1) to enable property mapping." + ) + + subtab1, subtab2 = st.tabs([" Images", "JSON Preview"]) + + with subtab1: + st.success( + f"Extracted {len(st.session_state.image_results)} plots" + ) + + col_img, col_json, col_all = st.columns(3) + + with col_img: + img_zip = create_zip(st.session_state.image_results, include_json=False) + st.download_button( + " Download Images Only", + data=img_zip, + file_name=f"{paper_id}_images.zip", + mime="application/zip", + use_container_width=True, + key="download_images", + ) + + with col_json: + json_data = [ + { + "caption": r["caption"], + "page": r["page"], + "image_count": len(r["image_data"]), + } + for r in st.session_state.image_results + ] + st.download_button( + " Download JSON", + data=json.dumps(json_data, indent=4), + file_name=f"{paper_id}_metadata.json", + mime="application/json", + use_container_width=True, + key="download_json_top", + ) + + with col_all: + full_zip = create_zip(st.session_state.image_results, include_json=True) + st.download_button( + " Download All", + data=full_zip, + file_name=f"{paper_id}_complete.zip", + mime="application/zip", + use_container_width=True, + key="download_all", + ) + + st.divider() + + if st.session_state.saved_image_mapping: + with st.expander(" Saved Image Mappings", expanded=False): + for img_key, mapping_info in st.session_state.saved_image_mapping.items(): + st.write( + f" **{mapping_info['caption']}** ? `{mapping_info['property']}`" + ) + st.write( + f" Saved as: `{mapping_info['filename']}`" + ) + st.divider() + + results_copy = st.session_state.image_results.copy() + + for idx in range(len(results_copy)): + if idx >= len(st.session_state.image_results): + break + + result = st.session_state.image_results[idx] + + with st.container(border=True): + col_cap, col_btn = st.columns([0.85, 0.15]) + col_cap.markdown( + f"**Page {result['page']}** - {result['caption']}" + ) + + if col_btn.button("Delete", key=f"del_g_{idx}_{result['page']}"): + del st.session_state.image_results[idx] + st.rerun() + + image_data_list = result["image_data"] + if image_data_list and len(image_data_list) > 0: + for p_idx in range(len(image_data_list)): + if p_idx >= len(st.session_state.image_results[idx]["image_data"]): + break + + img_data = st.session_state.image_results[idx]["image_data"][p_idx] + img_unique_key = f"{idx}_{p_idx}_{result['page']}" + + st.image(img_data["array"], width=300, channels="BGR") + + if has_extracted_data: + col_dropdown, col_add_btn, col_remove = st.columns( + [0.6, 0.2, 0.2] + ) + + with col_dropdown: + selected_property = st.selectbox( + "Select Property", + options=["-- Select --"] + property_list, + key=f"prop_select_{img_unique_key}", + label_visibility="collapsed", + ) + + with col_add_btn: + if st.button(" Add", key=f"add_btn_{img_unique_key}"): + if selected_property and selected_property != "-- Select --": + filepath = save_single_image_with_property( + img_data["array"], + mat_abbr, + selected_property, + save_dir="images", + ) + + st.session_state.saved_image_mapping[ + img_unique_key + ] = { + "property": selected_property, + "caption": result["caption"], + "filename": os.path.basename(filepath), + "path": filepath, + } + + st.success( + f" Saved as `{mat_abbr}_{selected_property}.png`" + ) + st.rerun() + else: + st.warning("Please select a property first") + + with col_remove: + if st.button("Remove", key=f"del_s_{img_unique_key}"): + if img_unique_key in st.session_state.saved_image_mapping: + del st.session_state.saved_image_mapping[img_unique_key] + + del st.session_state.image_results[idx]["image_data"][p_idx] + if len(st.session_state.image_results[idx]["image_data"]) == 0: + del st.session_state.image_results[idx] + st.rerun() + + if img_unique_key in st.session_state.saved_image_mapping: + mapping = st.session_state.saved_image_mapping[img_unique_key] + st.info(f"Mapped to: **{mapping['property']}**") + else: + col_info, col_remove = st.columns([0.8, 0.2]) + with col_info: + st.caption( + "Extract material data first to enable property mapping" + ) + with col_remove: + if st.button("Remove", key=f"del_s_{img_unique_key}"): + del st.session_state.image_results[idx]["image_data"][p_idx] + if len(st.session_state.image_results[idx]["image_data"]) == 0: + del st.session_state.image_results[idx] + st.rerun() + + st.divider() + + with subtab2: + st.subheader("Metadata Preview") + json_data = [ + { + "caption": r["caption"], + "page": r["page"], + "image_count": len(r["image_data"]), + "images": [img["filename"] for img in r["image_data"]], + } + for r in st.session_state.image_results + ] + + st.download_button( + " Download JSON", + data=json.dumps(json_data, indent=4), + file_name=f"{paper_id}_metadata.json", + mime="application/json", + key="download_json_bottom", + ) + + st.json(json_data) + else: + st.warning("No plots found in PDF") + + +main() diff --git a/page_files/categorized/Backend/Pdf_DataExtraction.py b/page_files/categorized/Backend/Pdf_DataExtraction.py new file mode 100644 index 0000000000000000000000000000000000000000..bb77f0f6712f190219d9c438ecba9fe9f9fa77b3 --- /dev/null +++ b/page_files/categorized/Backend/Pdf_DataExtraction.py @@ -0,0 +1,295 @@ +import streamlit as st +import pandas as pd +from PIL import Image +import requests +import base64 +import json +import os +from typing import Dict, Any, Optional + + + + +# Backend PDF extraction Logic +API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY") +API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key={API_KEY}" + +SCHEMA = { + "type": "OBJECT", + "properties": { + "material_name": {"type": "STRING"}, + "material_abbreviation": {"type": "STRING"}, + "mechanical_properties": { + "type": "ARRAY", + "items": { + "type": "OBJECT", + "properties": { + "section": {"type": "STRING"}, + "property_name": {"type": "STRING"}, + "value": {"type": "STRING"}, + "unit": {"type": "STRING"}, + "english": {"type": "STRING"}, + "test_condition": {"type": "STRING"}, + "comments": {"type": "STRING"} + }, + "required": ["section", "property_name", "value", "english", "comments"] + } + } + } +} + +# === GEMINI CALL FUNCTION === +def call_gemini_from_bytes(pdf_bytes: bytes, filename: str) -> Optional[Dict[str, Any]]: + """Calls Gemini API with PDF bytes""" + try: + encoded_file = base64.b64encode(pdf_bytes).decode("utf-8") + mime_type = "application/pdf" + except Exception as e: + st.error(f"Error encoding PDF: {e}") + return None + + prompt = ( + "Extract all experimental data from this research paper. " + "For each measurement, extract: " + "- experiment_name, measured_value, unit, uncertainty, method, conditions. " + "Return as JSON." + # "You are an expert materials scientist. From the attached PDF, extract the material name, " + # "abbreviation, and ALL properties across categories (Mechanical, Thermal, Electrical, Physical, " + # "Optical, Rheological, etc.). Return them as 'mechanical_properties' (a single list). " + # "For each property, you MUST extract:\n" + # "- property_name\n- value (or range)\n- unit\n" + # "- english (converted or alternate units, e.g., psi, °F, inches; write '' if not provided)\n" + # "- test_condition\n- comments (include any notes, footnotes, standards, remarks; write '' if none)\n" + # "All fields including english and comments are REQUIRED. Respond ONLY with valid JSON following the schema." + ) + + payload = { + "contents": [ + { + "parts": [ + {"text": prompt}, + {"inlineData": {"mimeType": mime_type, "data": encoded_file}} + ] + } + ], + "generationConfig": { + "temperature": 0, + "responseMimeType": "application/json", + "responseSchema": SCHEMA + } + } + + try: + r = requests.post(API_URL, json=payload, timeout=300) + r.raise_for_status() + data = r.json() + + candidates = data.get("candidates", []) + if not candidates: + return None + + parts = candidates[0].get("content", {}).get("parts", []) + json_text = None + for p in parts: + t = p.get("text", "") + if t.strip().startswith("{"): + json_text = t + break + + return json.loads(json_text) if json_text else None + except Exception as e: + st.error(f"Gemini API Error: {e}") + return None + + +def convert_to_dataframe(data: Dict[str, Any]) -> pd.DataFrame: + """Convert extracted JSON to DataFrame""" + rows = [] + for item in data.get("mechanical_properties", []): + rows.append({ + "material_name": data.get("material_name", ""), + "material_abbreviation": data.get("material_abbreviation", ""), + "section": item.get("section", ""), + "property_name": item.get("property_name", ""), + "value": item.get("value", ""), + "unit": item.get("unit", ""), + "english": item.get("english", ""), + "test_condition": item.get("test_condition", ""), + "comments": item.get("comments", "") + }) + return pd.DataFrame(rows) + + + +#using sentence transformers and semantic search techniques +import sqlite3 +import pandas as pd +import os +import requests +from sentence_transformers import SentenceTransformer +from sklearn.metrics.pairwise import cosine_similarity + +# ========================== +# CONFIGURATION +# ========================== +DB_PATH = "output_materials.db" +EXCEL_PATH = "5.1__actual.xlsx" +OUTPUT_EXCEL = "5.1__filled.xlsx" +GEMINI_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY") + +GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent" + + +# ========================== +# GEMINI YES/NO MATCH CHECK +# ========================== +def gemini_same_property(excel_prop, db_prop): + prompt = f""" +You are an expert materials scientist. Determine if BOTH property names refer +to the SAME mechanical property. + +Excel property: "{excel_prop}" +Database property: "{db_prop}" + +Rules: +- Compare meaning, not formatting. +- Ignore units, values, and numbers. +- If either refers to conditions, test setup, or non-property info, return NO. +- Return ONLY YES or NO. +""" + + payload = { + "contents": [{"parts": [{"text": prompt}]}] + } + + response = requests.post( + GEMINI_URL, + params={"key": GEMINI_KEY}, + json=payload, + timeout=60 + ).json() + + try: + ans = response["candidates"][0]["content"]["parts"][0]["text"].strip().upper() + except: + return False + + return ans == "YES" + + +# ========================== +# SEMANTIC MATCHER (fallback) +# ========================== +embed_model = SentenceTransformer("all-MiniLM-L6-v2") + +def semantic_match(excel_prop, df_section): + if df_section.empty: + return None + + # compute embeddings + db_props = df_section["property_name"].tolist() + db_vecs = embed_model.encode(db_props, convert_to_numpy=True) + q_vec = embed_model.encode([excel_prop], convert_to_numpy=True) + + sims = cosine_similarity(q_vec, db_vecs)[0] + + df_section = df_section.copy() + df_section["sim"] = sims + df_section = df_section.sort_values("sim", ascending=False) + + # Take top-5 candidates for Gemini check + top5 = df_section.head(5) + + for _, row in top5.iterrows(): + cand = row["property_name"] + if gemini_same_property(excel_prop, cand): + return row + + return None + + +# ========================== +# MAIN PIPELINE +# ========================== +conn = sqlite3.connect(DB_PATH) + +# Get material tables +tables = pd.read_sql_query( + "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';", + conn +)["name"].tolist() + +print(f"Detected tables: {tables}") + +# Load Excel template once +df_excel_template = pd.read_excel(EXCEL_PATH) +cols = df_excel_template.columns.tolist() + +section_col = next((c for c in cols if "section" in c.lower()), None) +prop_col = next((c for c in cols if "property" in c.lower()), cols[0]) + +print(f"Detected section column: {section_col}") +print(f"Detected property column: {prop_col}") + +with pd.ExcelWriter(OUTPUT_EXCEL, engine="openpyxl") as writer: + + for table in tables: + print(f"\nProcessing table: {table}") + + # Load DB table + df_db = pd.read_sql_query(f""" + SELECT section, property_name, value, unit, english, comments + FROM '{table}' + """, conn) + + df_excel = df_excel_template.copy() + df_excel["Matched Property"] = "" + df_excel["Value"] = "" + df_excel["Unit"] = "" + df_excel["English"] = "" + df_excel["Comments"] = "" + + # Process each Excel property + for i, row in df_excel.iterrows(): + excel_prop = str(row[prop_col]).strip() + excel_section = str(row.get(section_col, "")).strip().lower() + + + if section_col: + df_sec = df_db[df_db["section"].str.lower() == excel_section] + else: + df_sec = df_db + + # ========================== + # 1️ EXACT MATCH + # ========================== + exact = df_sec[df_sec["property_name"].str.lower() == excel_prop.lower()] + + if not exact.empty: + r = exact.iloc[0] + df_excel.at[i, "Matched Property"] = r["property_name"] + df_excel.at[i, "Value"] = r["value"] + df_excel.at[i, "Unit"] = r["unit"] + df_excel.at[i, "English"] = r["english"] + df_excel.at[i, "Comments"] = r["comments"] + continue # done + + # ========================== + # 2️ SEMANTIC + GEMINI MATCH + # ========================== + best = semantic_match(excel_prop, df_sec) + + if best is not None: + df_excel.at[i, "Matched Property"] = best["property_name"] + df_excel.at[i, "Value"] = best["value"] + df_excel.at[i, "Unit"] = best["unit"] + df_excel.at[i, "English"] = best["english"] + df_excel.at[i, "Comments"] = best["comments"] + else: + df_excel.at[i, "Matched Property"] = "" + + # Write one sheet per material + df_excel.to_excel(writer, sheet_name=table[:31], index=False) + +print(f"\nDONE → Final filled Excel: {OUTPUT_EXCEL}") +conn.close() diff --git a/page_files/categorized/Backend/Pdf_ImageExtraction.py b/page_files/categorized/Backend/Pdf_ImageExtraction.py new file mode 100644 index 0000000000000000000000000000000000000000..03d9aee27a27e2044a2a01075198d73edd5a9356 --- /dev/null +++ b/page_files/categorized/Backend/Pdf_ImageExtraction.py @@ -0,0 +1,390 @@ +import os +import re +import json +import math +import tempfile +import fitz # PyMuPDF +import cv2 +import numpy as np +from PIL import Image +import streamlit as st + +# ------------------- +# Config +# ------------------- +DPI = 300 +OUT_DIR = "outputs" + +KEEP_ONLY_STRESS_STRAIN = False + +CAP_RE = re.compile(r"^(Fig\.?\s*\d+|Figure\s*\d+)\b", re.IGNORECASE) +SS_KW = re.compile( + r"(stress\s*[-–]?\s*strain|stress|strain|tensile|MPa|GPa|kN|yield|elongation)", + re.IGNORECASE +) + +# ------------------- +# Render helpers +# ------------------- +def render_page(page, dpi=DPI): + mat = fitz.Matrix(dpi/72, dpi/72) + pix = page.get_pixmap(matrix=mat, alpha=False) + img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) + return img, mat + +def pdf_to_px_bbox(bbox_pdf, mat): + x0, y0, x1, y1 = bbox_pdf + sx, sy = mat.a, mat.d + return (int(float(x0) * sx), int(float(y0) * sy), int(float(x1) * sx), int(float(y1) * sy)) + +def safe_crop_px(pil_img, box): + if not isinstance(box, (tuple, list)): + return None + if len(box) == 1 and isinstance(box[0], (tuple, list)) and len(box[0]) == 4: + box = box[0] + if len(box) != 4: + return None + + x0, y0, x1, y1 = box + if any(isinstance(v, (tuple, list)) for v in (x0, y0, x1, y1)): + return None + + try: + x0 = int(x0) + y0 = int(y0) + x1 = int(x1) + y1 = int(y1) + except (TypeError, ValueError): + return None + + if x1 < x0: + x0, x1 = x1, x0 + if y1 < y0: + y0, y1 = y1, y0 + + W, H = pil_img.size + x0 = max(0, min(W, x0)) + x1 = max(0, min(W, x1)) + y0 = max(0, min(H, y0)) + y1 = max(0, min(H, y1)) + if x1 <= x0 or y1 <= y0: + return None + return pil_img.crop((x0, y0, x1, y1)) + +# ------------------- +# Captions +# ------------------- +def find_caption_blocks(page): + caps = [] + blocks = page.get_text("blocks") + for b in blocks: + x0, y0, x1, y1, text = b[0], b[1], b[2], b[3], b[4] + t = " ".join(str(text).strip().split()) + if CAP_RE.match(t): + caps.append({"bbox": (x0, y0, x1, y1), "text": t}) + return caps + +# ------------------- +# Dedupe: dHash +# ------------------- +def dhash64(pil_img): + gray = pil_img.convert("L").resize((9, 8), Image.LANCZOS) + pixels = list(gray.getdata()) + bits = 0 + for r in range(8): + for c in range(8): + left = pixels[r * 9 + c] + right = pixels[r * 9 + c + 1] + bits = (bits << 1) | (1 if left > right else 0) + return bits + +# ------------------- +# Rejectors +# ------------------- +def has_colorbar_like_strip(pil_img): + img = np.array(pil_img) + if img.ndim != 3: + return False + H, W, _ = img.shape + if W < 250 or H < 150: + return False + strip_w = max(18, int(0.07 * W)) + strip = img[:, W-strip_w:W, :] + q = (strip // 24).reshape(-1, 3) + uniq = np.unique(q, axis=0) + return len(uniq) > 70 + +def texture_score(pil_img): + gray = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2GRAY) + lap = cv2.Laplacian(gray, cv2.CV_64F) + return float(lap.var()) + +def is_mostly_legend(pil_img): + gray = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2GRAY) + bw = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] + bw = cv2.medianBlur(bw, 3) + H, W = bw.shape + fill = float(np.count_nonzero(bw)) / float(H * W) + return (0.03 < fill < 0.18) and (min(H, W) < 260) + +# ------------------- +# Plot detection +# ------------------- +def detect_axes_lines(pil_img): + gray = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2GRAY) + edges = cv2.Canny(gray, 50, 150) + H, W = gray.shape + min_len = int(0.28 * min(H, W)) + + lines = cv2.HoughLinesP( + edges, 1, np.pi/180, + threshold=90, + minLineLength=min_len, + maxLineGap=14 + ) + if lines is None: + return None, None + + horizontals, verticals = [], [] + for x1, y1, x2, y2 in lines[:, 0]: + dx, dy = abs(x2-x1), abs(y2-y1) + length = math.hypot(dx, dy) + if dy < 18 and dx > 0.35 * W: + horizontals.append((length, (x1, y1, x2, y2))) + if dx < 18 and dy > 0.35 * H: + verticals.append((length, (x1, y1, x2, y2))) + + if not horizontals or not verticals: + return None, None + + horizontals.sort(key=lambda t: t[0], reverse=True) + verticals.sort(key=lambda t: t[0], reverse=True) + return horizontals[0][1], verticals[0][1] + +def axis_intersection_ok(x_axis, y_axis, W, H): + xa_y = int(round((x_axis[1] + x_axis[3]) / 2)) + ya_x = int(round((y_axis[0] + y_axis[2]) / 2)) + if not (0 <= xa_y < H and 0 <= ya_x < W): + return False + if ya_x > int(0.95 * W) or xa_y < int(0.05 * H): + return False + return True + +def tick_text_presence_score(pil_img, x_axis, y_axis): + img = np.array(pil_img) + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + bw = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] + bw = cv2.medianBlur(bw, 3) + + H, W = gray.shape + xa_y = int(round((x_axis[1] + x_axis[3]) / 2)) + ya_x = int(round((y_axis[0] + y_axis[2]) / 2)) + + y0a = max(0, xa_y - 40) + y1a = min(H, xa_y + 110) + x_roi = bw[y0a:y1a, 0:W] + + x0b = max(0, ya_x - 180) + x1b = min(W, ya_x + 50) + y_roi = bw[0:H, x0b:x1b] + + def count_small_components(mask): + num, _, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8) + cnt = 0 + for i in range(1, num): + x, y, w, h, area = stats[i] + if 4 <= w <= 150 and 4 <= h <= 150 and 20 <= area <= 5000: + cnt += 1 + return cnt + + return count_small_components(x_roi) + count_small_components(y_roi) + +def is_real_plot(pil_img): + if has_colorbar_like_strip(pil_img): + return False + if is_mostly_legend(pil_img): + return False + + x_axis, y_axis = detect_axes_lines(pil_img) + if x_axis is None or y_axis is None: + return False + + arr = np.array(pil_img) + H, W = arr.shape[0], arr.shape[1] + if not axis_intersection_ok(x_axis, y_axis, W, H): + return False + + if texture_score(pil_img) > 2200: + return False + + score = tick_text_presence_score(pil_img, x_axis, y_axis) + return score >= 18 + +# ------------------- +# Candidate boxes in a region +# ------------------- +def connected_components_boxes(pil_img): + img_bgr = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR) + gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) + mask = (gray < 245).astype(np.uint8) * 255 + mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((7, 7), np.uint8), iterations=2) + num, _, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8) + + boxes = [] + for i in range(1, num): + x, y, w, h, area = stats[i] + boxes.append((int(area), (int(x), int(y), int(x + w), int(y + h)))) + boxes.sort(key=lambda t: t[0], reverse=True) + return boxes + +def expand_box(box, W, H, left=0.10, right=0.06, top=0.06, bottom=0.18): + x0, y0, x1, y1 = box + bw = x1 - x0 + bh = y1 - y0 + ex0 = max(0, int(x0 - left * bw)) + ex1 = min(W, int(x1 + right * bw)) + ey0 = max(0, int(y0 - top * bh)) + ey1 = min(H, int(y1 + bottom * bh)) + return (ex0, ey0, ex1, ey1) + +# ------------------- +# Crop plot from caption +# ------------------- +def crop_plot_from_caption(page_img, cap_bbox_pdf, mat): + cap_px = pdf_to_px_bbox(cap_bbox_pdf, mat) + cap_y0 = cap_px[1] + cap_y1 = cap_px[3] + + W, H = page_img.size + search_top = max(0, cap_y0 - int(0.95 * H)) + search_bot = min(H, cap_y1 + int(0.20 * H)) + region = safe_crop_px(page_img, (0, search_top, W, search_bot)) + if region is None: + return None + + comps = connected_components_boxes(region) + best = None + best_area = -1 + + for area, box in comps[:35]: + x0, y0, x1, y1 = box + bw = x1 - x0 + bh = y1 - y0 + if bw < 220 or bh < 180: + continue + + exp = expand_box(box, region.size[0], region.size[1]) + cand = safe_crop_px(region, exp) + if cand is None: + continue + + if not is_real_plot(cand): + continue + + if area > best_area: + best_area = area + best = cand + + return best + +# ------------------- +# Streamlit UI +# ------------------- +def run_extraction(pdf_path, paper_id="uploaded_paper"): + out_paper = os.path.join(OUT_DIR, paper_id) + out_imgs = os.path.join(out_paper, "plots_with_axes") + os.makedirs(out_imgs, exist_ok=True) + + doc = fitz.open(pdf_path) + results = [] + seen = set() + saved = 0 + + for p in range(len(doc)): + page = doc[p] + caps = find_caption_blocks(page) + if not caps: + continue + + page_img, mat = render_page(page, dpi=DPI) + + for cap in caps: + cap_text = cap["text"] + + if KEEP_ONLY_STRESS_STRAIN and not SS_KW.search(cap_text): + continue + + fig = crop_plot_from_caption(page_img, cap["bbox"], mat) + if fig is None: + continue + + if fig.size[0] > 8 and fig.size[1] > 8: + fig = fig.crop((2, 2, fig.size[0]-2, fig.size[1]-2)) + + try: + h = dhash64(fig) + except Exception: + continue + + if h in seen: + continue + seen.add(h) + + img_name = f"p{p+1:02d}_{saved:04d}.png" + img_path = os.path.join(out_imgs, img_name) + fig.save(img_path) + + results.append({ + "page": p + 1, + "caption": cap_text, + "image": img_path + }) + saved += 1 + + out_json = os.path.join(out_paper, "plots_with_axes.json") + with open(out_json, "w", encoding="utf-8") as f: + json.dump(results, f, indent=2, ensure_ascii=False) + + return results, out_json + +def main(): + st.set_page_config(page_title="Research Paper Plot Extractor", layout="wide") + st.title(" Plot Extractor (Upload PDF)") + + uploaded = st.file_uploader("Upload a research paper PDF", type=["pdf"]) + if not uploaded: + st.info("Upload a PDF to extract plots.") + return + + paper_id = os.path.splitext(uploaded.name)[0].replace(" ", "_") + + with tempfile.TemporaryDirectory() as tmpdir: + pdf_path = os.path.join(tmpdir, uploaded.name) + with open(pdf_path, "wb") as f: + f.write(uploaded.read()) + + with st.spinner("Extracting plots..."): + results, out_json = run_extraction(pdf_path, paper_id=paper_id) + + st.success(f"Extracted {len(results)} plots.") + + # Show images + captions + for r in results: + st.markdown(f"**Page {r['page']}** — {r['caption']}") + st.image(r["image"], use_container_width=True) + st.divider() + + # JSON viewer + download + st.subheader("JSON Output") + st.json(results) + + with open(out_json, "rb") as f: + st.download_button( + "Download JSON", + data=f, + file_name=os.path.basename(out_json), + mime="application/json" + ) + +if __name__ == "__main__": + main() diff --git a/page_files/categorized/Backend/upload_backend.py b/page_files/categorized/Backend/upload_backend.py new file mode 100644 index 0000000000000000000000000000000000000000..a3d2eeaaadfd8db0fe11338cf3c20a0c7697c535 --- /dev/null +++ b/page_files/categorized/Backend/upload_backend.py @@ -0,0 +1,356 @@ +import os +import re +import json +import zipfile +from io import BytesIO +from typing import Dict, Any, Optional +from collections import defaultdict + +import cv2 +import fitz # PyMuPDF +import numpy as np +import pandas as pd +import requests +import streamlit as st +import base64 + +API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY") +API_URL = ( + "https://generativelanguage.googleapis.com/v1beta/" + "models/gemini-2.5-flash-preview-09-2025:generateContent?key=" + f"{API_KEY}" + if API_KEY + else None +) + +SCHEMA = { + "type": "OBJECT", + "properties": { + "material_name": {"type": "STRING"}, + "material_abbreviation": {"type": "STRING"}, + "trade_grade": { + "type": "STRING", + "description": "Commercial or trade grade name of the material; '' if not provided", + }, + "manufacturer": { + "type": "STRING", + "description": "Company or organization producing the material; '' if not provided", + }, + "mechanical_properties": { + "type": "ARRAY", + "items": { + "type": "OBJECT", + "properties": { + "section": {"type": "STRING"}, + "property_name": {"type": "STRING"}, + "value": {"type": "STRING"}, + "unit": {"type": "STRING"}, + "english": {"type": "STRING"}, + "test_condition": {"type": "STRING"}, + "comments": {"type": "STRING"}, + }, + "required": [ + "section", + "property_name", + "value", + "english", + "comments", + ], + }, + }, + }, +} + +DPI = 300 +CAP_RE = re.compile(r"^(Fig\.?\s*\d+|Figure\s*\d+)\b", re.IGNORECASE) + + +def make_abbreviation(name: str) -> str: + if not name: + return "UNKNOWN" + words = name.split() + abbr = "".join(w[0] for w in words if w and w[0].isalpha()).upper() + return abbr or name[:6].upper() + + +def call_gemini_from_bytes(pdf_bytes: bytes, filename: str) -> Optional[Dict[str, Any]]: + if not API_KEY or not API_URL: + st.error("Missing Gemini API key. Set GEMINI_API_KEY in environment variables.") + return None + + try: + encoded_file = base64.b64encode(pdf_bytes).decode("utf-8") + mime_type = "application/pdf" + except Exception as exc: + st.error(f"Error encoding PDF: {exc}") + return None + + prompt = ( + "You are an expert materials scientist. From the attached PDF, extract:\n" + "- material_name (generic material, e.g., isotactic polypropylene)\n" + "- material_abbreviation\n" + "- trade_grade (commercial or trade name; write '' if not provided)\n" + "- manufacturer (company or organization producing the material; write '' if not provided)\n\n" + "Extract ALL properties across categories (Mechanical, Thermal, Electrical, Physical, " + "Optical, Rheological, etc.) and return them as 'mechanical_properties' (a single list).\n\n" + "For each property, you MUST extract:\n" + "- property_name\n" + "- value (or range)\n" + "- unit\n" + "- english (converted or alternate units, e.g., psi, °F, inches; write '' if not provided)\n" + "- test_condition\n" + "- comments (include any notes, footnotes, standards, remarks; write '' if none)\n\n" + "All fields including english and comments are REQUIRED.\n" + "Respond ONLY with valid JSON following the schema." + ) + + payload = { + "contents": [ + { + "parts": [ + {"text": prompt}, + {"inlineData": {"mimeType": mime_type, "data": encoded_file}}, + ] + } + ], + "generationConfig": { + "temperature": 0, + "responseMimeType": "application/json", + "responseSchema": SCHEMA, + }, + } + + try: + response = requests.post(API_URL, json=payload, timeout=300) + response.raise_for_status() + data = response.json() + + candidates = data.get("candidates", []) + if not candidates: + return None + + parts = candidates[0].get("content", {}).get("parts", []) + json_text = None + for part in parts: + text = part.get("text", "") + if text.strip().startswith("{"): + json_text = text + break + + return json.loads(json_text) if json_text else None + except Exception as exc: + st.error(f"Gemini API Error: {exc}") + return None + + +def convert_to_dataframe(data: Dict[str, Any]) -> pd.DataFrame: + mat_name = data.get("material_name", "") or "" + mat_abbr = data.get("material_abbreviation", "") or "" + trade_grade = data.get("trade_grade", "") or "" + manufacturer = data.get("manufacturer", "") or "" + + if not mat_abbr: + mat_abbr = make_abbreviation(mat_name) + + rows = [] + for item in data.get("mechanical_properties", []): + rows.append( + { + "material_name": mat_name, + "material_abbreviation": mat_abbr, + "trade_grade": trade_grade, + "manufacturer": manufacturer, + "section": item.get("section", "") or "Mechanical", + "property_name": item.get("property_name", "") or "Unknown property", + "value": item.get("value", "") or "N/A", + "unit": item.get("unit", "") or "", + "english": item.get("english", "") or "", + "test_condition": item.get("test_condition", "") or "", + "comments": item.get("comments", "") or "", + } + ) + return pd.DataFrame(rows) + + +def get_page_image(page): + pix = page.get_pixmap(matrix=fitz.Matrix(DPI / 72, DPI / 72)) + img = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, 3) + return cv2.cvtColor(img, cv2.COLOR_RGB2BGR) + + +def is_valid_plot_geometry(binary_crop): + height, width = binary_crop.shape + if height < 100 or width < 100: + return False + ink_density = cv2.countNonZero(binary_crop) / (width * height) + if ink_density > 0.35: + return False + h_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (width // 4, 1)) + v_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, height // 4)) + has_h = cv2.countNonZero(cv2.erode(binary_crop, h_kernel, iterations=1)) > 0 + has_v = cv2.countNonZero(cv2.erode(binary_crop, v_kernel, iterations=1)) > 0 + return has_h or has_v + + +def merge_boxes(rects): + if not rects: + return [] + rects = sorted(rects, key=lambda r: r[2] * r[3], reverse=True) + merged = [] + for rect in rects: + rx, ry, rw, rh = rect + if not any( + rx >= m[0] - 15 + and ry >= m[1] - 15 + and rx + rw <= m[0] + m[2] + 15 + and ry + rh <= m[1] + m[3] + 15 + for m in merged + ): + merged.append(rect) + return merged + + +def extract_images(pdf_doc): + grouped_data = defaultdict(lambda: {"page": 0, "image_data": []}) + padding = 30 + + for page_num, page in enumerate(pdf_doc, start=1): + img_bgr = get_page_image(page) + gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) + _, binary = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV) + kernel = np.ones((10, 10), np.uint8) + dilated = cv2.dilate(binary, kernel, iterations=1) + contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + candidates = [] + page_h, page_w = gray.shape + for cnt in contours: + x, y, w, h = cv2.boundingRect(cnt) + if 0.03 < (w * h) / (page_w * page_h) < 0.8: + if is_valid_plot_geometry(binary[y : y + h, x : x + w]): + candidates.append((x, y, w, h)) + + final_rects = merge_boxes(candidates) + blocks = page.get_text("blocks") + + for (cx, cy, cw, ch) in final_rects: + best_caption = f"Figure on Page {page_num} (Unlabeled)" + min_dist = float("inf") + for block in blocks: + text = block[4].strip() + if CAP_RE.match(text): + cap_y = block[1] * (DPI / 72) + dist = cap_y - (cy + ch) + if 0 < dist < (page_h * 0.3) and dist < min_dist: + best_caption = text.replace("\n", " ") + min_dist = dist + + x1, y1 = max(0, cx - padding), max(0, cy - padding) + x2, y2 = min(page_w, cx + cw + padding), min(page_h, cy + ch + padding) + crop = img_bgr[int(y1) : int(y2), int(x1) : int(x2)] + + _, buffer = cv2.imencode(".png", crop) + img_bytes = buffer.tobytes() + fname = f"pg{page_num}_{cx}_{cy}.png" + + grouped_data[best_caption]["page"] = page_num + grouped_data[best_caption]["image_data"].append( + {"filename": fname, "bytes": img_bytes, "array": crop} + ) + + return [ + {"caption": key, "page": value["page"], "image_data": value["image_data"]} + for key, value in grouped_data.items() + ] + + +def create_zip(results, include_json=True): + buf = BytesIO() + with zipfile.ZipFile(buf, "w") as zf: + if include_json: + json_data = [ + {"caption": item["caption"], "page": item["page"], "image_count": len(item["image_data"])} + for item in results + ] + zf.writestr("plot_data.json", json.dumps(json_data, indent=4)) + + for item in results: + for img_data in item["image_data"]: + zf.writestr(img_data["filename"], img_data["bytes"]) + + buf.seek(0) + return buf.getvalue() + + +def match_caption_to_property(caption: str, property_name: str) -> bool: + caption_lower = caption.lower() + prop_lower = property_name.lower() + + if prop_lower in caption_lower: + return True + + keyword_map = { + "tensile modulus": ["tensile", "modulus", "young", "elastic"], + "tensile strength": ["tensile", "strength", "ultimate"], + "elongation at break": ["elongation", "strain", "break"], + "glass transition temperature": ["glass transition", "tg", "transition"], + "melting temperature": ["melting", "tm", "melt"], + "density": ["density", "specific gravity"], + "impact strength": ["impact", "izod", "charpy"], + "flexural modulus": ["flexural", "bending", "flex"], + "stress": ["stress", "strain"], + "thermal": ["thermal", "temperature", "heat"], + "crystallinity": ["crystallinity", "crystalline", "xrd"], + } + + for prop_key, keywords in keyword_map.items(): + if prop_key in prop_lower and any(kw in caption_lower for kw in keywords): + return True + + prop_words = set(prop_lower.replace("(", "").replace(")", "").split()) + caption_words = set(caption_lower.replace("(", "").replace(")", "").split()) + + common_words = prop_words & caption_words + significant_words = common_words - {"the", "of", "at", "in", "a", "an"} + + return len(significant_words) >= 2 + + +def save_matched_images(df: pd.DataFrame, image_results: list, save_dir: str = "images"): + os.makedirs(save_dir, exist_ok=True) + saved_images = [] + + if df.empty: + return saved_images + + mat_abbr = df.iloc[0]["material_abbreviation"] + properties = df["property_name"].unique() + matched_properties = set() + + for img_result in image_results: + caption = img_result["caption"] + + for prop in properties: + if prop in matched_properties: + continue + if match_caption_to_property(caption, prop): + if img_result["image_data"]: + first_img = img_result["image_data"][0] + filename = f"{mat_abbr}_{prop}.png" + filepath = os.path.join(save_dir, filename) + cv2.imwrite(filepath, first_img["array"]) + saved_images.append({"property": prop, "caption": caption, "path": filepath}) + matched_properties.add(prop) + break + + return saved_images + + +def save_single_image_with_property( + img_array, mat_abbr: str, property_name: str, save_dir: str = "images" +) -> str: + os.makedirs(save_dir, exist_ok=True) + filename = f"{mat_abbr}_{property_name}.png" + filepath = os.path.join(save_dir, filename) + cv2.imwrite(filepath, img_array) + return filepath diff --git a/page_files/categorized/ESS-min.jpg b/page_files/categorized/ESS-min.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a0e7ea1d745fbce5e0d9cbd9940b3a246c7526b --- /dev/null +++ b/page_files/categorized/ESS-min.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff58c9304c39dc90ca15b516a1f1ec385ea60a9829c5dd9eb698ee1f82778eb7 +size 356048 diff --git a/page_files/categorized/page1.py b/page_files/categorized/page1.py new file mode 100644 index 0000000000000000000000000000000000000000..e1d627ce5730fe88b9d35e96969c851e2f5fcd4c --- /dev/null +++ b/page_files/categorized/page1.py @@ -0,0 +1,345 @@ +import streamlit as st +import pandas as pd +from PIL import Image +import re +import base64 + +with open("images/Materials_bg_InDeS.png", "rb") as f: + encoded = base64.b64encode(f.read()).decode() + +image_url = f"data:image/png;base64,{encoded}" + +st.set_page_config(initial_sidebar_state="expanded") + + +def extract_matrix_fiber_from_abbr(abbr: str): + if not isinstance(abbr, str): + return None, None + + text = abbr.lower() + + matrix_map = { + "epoxy": "Epoxy", + "cyanate ester": "Cyanate Ester", + "cynate ester": "Cyanate Ester", + "polypropylene": "Polypropylene", + "pp": "Polypropylene", + "peek": "PEEK", + "pei": "PEI", + "nylon": "Nylon", + "pa6": "PA6", + "polyester": "Polyester", + "vinyl ester": "Vinyl Ester", + "phenolic": "Phenolic" + } + + matrix = None + for key, val in matrix_map.items(): + if key in text: + matrix = val + break + + fiber_map = { + "carbon": "Carbon Fiber", + "glass": "Glass Fiber", + "e-glass": "E-Glass Fiber", + "s-glass": "S-Glass Fiber", + "aramid": "Aramid Fiber", + "kevlar": "Kevlar Fiber", + "basalt": "Basalt Fiber", + "natural": "Natural Fiber" + } + + fiber = None + for key, val in fiber_map.items(): + if key in text: + fiber = val + break + + return matrix, fiber + + +def main(): + st.markdown(f""" + +
+

Categorized Material Search

+

+ Explore the AIM materials database by browsing polymers, fibers, and composites in a structured, category-driven view.
+ Use the filters to narrow down materials by class, composition, and property type, then inspect detailed property data and + associated experimental plots where available. +

+
+ """, + unsafe_allow_html=True + ) + + mat_section = st.sidebar.expander("Materials", expanded=False) + with mat_section: + thermo = mat_section.button("Composites") + polymers = mat_section.button("Polymers") + Fibers = mat_section.button("Fibers") + + if "material_type" not in st.session_state: + st.session_state.material_type = "Composites" + + if thermo: + st.session_state.material_type = "Composites" + elif polymers: + st.session_state.material_type = "Polymers" + elif Fibers: + st.session_state.material_type = "Fibers" + + @st.cache_data + def load_data(material_type): + file_map = { + "Composites": "data/Composites_material_data.csv", + "Polymers": "data/polymers_material_data.csv", + "Fibers": "data/Fibers_material_data.csv", + } + return pd.read_csv(file_map[material_type]) + + csv_data = load_data(st.session_state.material_type) + + CLASS_MAP = { + "Polymers": "Polymer", + "Fibers": "Fiber", + "Composites": "Composite", + } + + current_class = CLASS_MAP[st.session_state.material_type] + + if "user_uploaded_data" in st.session_state: + user_df = st.session_state["user_uploaded_data"] + filtered_user_df = user_df[ + user_df["material_class"] == current_class + ] + df = pd.concat([csv_data, filtered_user_df], ignore_index=True) + else: + df = csv_data + + st.session_state["base_data"] = df + + st.title("Materials DataSet") + + materials_df = ( + df[["material_abbreviation", "material_name"]] + .fillna("") + .drop_duplicates() + .reset_index(drop=True) + ) + + materials_df[["Matrix", "Fiber"]] = materials_df["material_abbreviation"].apply( + lambda x: pd.Series(extract_matrix_fiber_from_abbr(x)) + ) + + if "search_term" in st.session_state: + term = st.session_state.pop("search_term") + try: + pattern = re.compile(term, re.IGNORECASE) + except re.error: + pattern = re.compile(re.escape(term), re.IGNORECASE) + mask = ( + materials_df["material_abbreviation"].astype(str).str.contains(pattern) | + materials_df["material_name"].astype(str).str.contains(pattern) + ) + filtered_materials_df = materials_df[mask].reset_index(drop=True) + else: + filtered_materials_df = materials_df.copy() + + def get_selected_value(df, key, column_name): + if key in st.session_state: + sel = st.session_state[key]["selection"]["cells"] + if sel: + row_idx = sel[0][0] + return df.iloc[row_idx][column_name] + return None + + mat = get_selected_value(filtered_materials_df, "material_table", "material_abbreviation") + + properties_df = pd.DataFrame(columns=["property_name", "section"]) + + prop_col, _ = st.columns([4, 6]) + with st.container(border=True): + with prop_col: + with st.expander("Select Property", expanded=bool(mat)): + if mat: + filtered_df = df[ + (df["material_abbreviation"] == mat) & + (df["value"].notna()) & + (df["property_name"].notna()) + ] + else: + filtered_df = df[df["value"].notna() & df["property_name"].notna()] + + available_sections = list(filtered_df["section"].drop_duplicates()) + + if "selected_section" in st.session_state: + incoming = st.session_state.pop("selected_section") + + matched = incoming if incoming in available_sections else None + + if not matched: + incoming_lower = incoming.lower() + for s in available_sections: + if s.lower() == incoming_lower: + matched = s + break + + if matched: + if "property_selectbox" in st.session_state: + del st.session_state["property_selectbox"] + st.session_state["property_selectbox"] = matched + + property_sel = st.selectbox( + "Type of Property", + available_sections, + key="property_selectbox" + ) + + properties_df = ( + filtered_df[filtered_df["section"] == property_sel][["property_name", "section"]] + .drop_duplicates() + .reset_index(drop=True) + ) + + st.dataframe( + properties_df.style.set_properties(**{ + 'background-color': 'white', + 'color': "#c8c0c0", + 'border': '1px solid #e0e0e0', + 'font-family': 'monospace', + 'font-size': '12px' + }).set_table_styles([ + { + 'selector': 'thead tr th', + 'props': [ + ('background-color', '#f5f5f5'), + ('color', "#C8C4C4"), + ('font-weight', '600'), + ('border-bottom', '2px solid #d0d0d0'), + ('font-family', 'monospace'), + ] + }, + { + 'selector': 'tbody tr:hover', + 'props': [('background-color', '#f9f9f9')] + } + ]), + key="property_table", + selection_mode="single-cell", + on_select="rerun", + use_container_width=True, + height=300 + ) + + selected_matrix = "All" + selected_fiber = "All" + + if st.session_state.material_type == "Composites": + matrix_options = sorted(filtered_materials_df["Matrix"].dropna().unique()) + fiber_options = sorted(filtered_materials_df["Fiber"].dropna().unique()) + + fcol1, fcol2, _ = st.columns([2, 2, 6]) + with fcol1: + selected_matrix = st.selectbox("Matrix Material", ["All"] + matrix_options) + with fcol2: + selected_fiber = st.selectbox("Fiber Material", ["All"] + fiber_options) + + if st.session_state.material_type == "Composites": + if selected_matrix != "All": + filtered_materials_df = filtered_materials_df[ + filtered_materials_df["Matrix"] == selected_matrix + ] + + if selected_fiber != "All": + filtered_materials_df = filtered_materials_df[ + filtered_materials_df["Fiber"] == selected_fiber + ] + + st.dataframe( + filtered_materials_df.style.set_properties(**{ + 'background-color': 'white', + 'color': "#d3cccc", + 'border': '1px solid #e0e0e0', + 'font-family': 'monospace', + 'font-size': '12px' + }).set_table_styles([ + { + 'selector': 'thead tr th', + 'props': [ + ('background-color', '#f5f5f5'), + ('color', "#D3C9C9"), + ('font-weight', '600'), + ('border-bottom', '2px solid #d0d0d0'), + ('font-family', 'monospace'), + ] + }, + { + 'selector': 'tbody tr:hover', + 'props': [('background-color', '#f9f9f9')] + } + ]), + key="material_table", + selection_mode="single-cell", + on_select="rerun", + use_container_width=True, + height=500 + ) + + prop = get_selected_value(properties_df, "property_table", "property_name") + + st.write("") + if st.button("Search", disabled=not (mat and prop)): + st.write(f"**Material:** {mat}") + st.write(f"**Property:** {prop}") + + result = df[ + (df["material_abbreviation"] == mat) & + (df["property_name"] == prop) & + (df["value"].notna()) + ] + + if not result.empty: + st.subheader("Property Data") + st.dataframe(result.T, use_container_width=True) + + st.subheader("Property Graph") + img_path = f"images/{mat}_{prop}.png" + + try: + img = Image.open(img_path) + st.image(img, use_container_width=True, caption="Stress strain curve") + except FileNotFoundError: + st.write("") + else: + st.warning("No data found for this material-property combination") \ No newline at end of file diff --git a/page_files/categorized/page2.py b/page_files/categorized/page2.py new file mode 100644 index 0000000000000000000000000000000000000000..d6ac3c99dbf3c51dba4d8bab9b6a945e336271d7 --- /dev/null +++ b/page_files/categorized/page2.py @@ -0,0 +1,265 @@ +import streamlit as st +import pandas as pd +import os +from PIL import Image +import boto3 +import tabula +import faiss +import json +import base64 +import pymupdf +import requests +import os +import logging +import numpy as np +import warnings +from tqdm import tqdm +from botocore.exceptions import ClientError +from langchain_text_splitters import RecursiveCharacterTextSplitter +from IPython import display +from langchain_aws import ChatBedrock + + +from pathlib import Path + +def main(): + + + + + logger = logging.getLogger(__name__) + logger.setLevel(logging.ERROR) + + warnings.filterwarnings("ignore") + + def create_directories(base_dir): + directories = ["images", "text", "tables", "page_images"] + for dir in directories: + os.makedirs(os.path.join(base_dir, dir), exist_ok=True) + + + def process_tables(doc, page_num, base_dir, items): + try: + tables = tabula.read_pdf(filepath, pages=page_num + 1, multiple_tables=True) + if not tables: + return + for table_idx, table in enumerate(tables): + table_text = "\n".join([" | ".join(map(str, row)) for row in table.values]) + table_file_name = f"{base_dir}/tables/{os.path.basename(filepath)}_table_{page_num}_{table_idx}.txt" + with open(table_file_name, 'w') as f: + f.write(table_text) + items.append({"page": page_num, "type": "table", "text": table_text, "path": table_file_name}) + except Exception as e: + print(f"Error extracting tables from page {page_num}: {str(e)}") + + doc = pymupdf.open(filepath) + num_pages = len(doc) + base_dir = "data" + + # Creating the directories + create_directories(base_dir) + text_splitter = RecursiveCharacterTextSplitter(chunk_size=700, chunk_overlap=200, length_function=len) + items = [] + + # Process each page of the PDF + for page_num in tqdm(range(num_pages), desc="Processing PDF pages"): + page = doc[page_num] + process_tables(doc, page_num, base_dir, items) + + [i for i in items if i['type'] == 'table'][0] + # Generating Multimodal Embeddings using Amazon Titan Multimodal Embeddings model + def generate_multimodal_embeddings(prompt=None, image=None, output_embedding_length=384): + """ + Invoke the Amazon Titan Multimodal Embeddings model using Amazon Bedrock runtime. + + Args: + prompt (str): The text prompt to provide to the model. + image (str): A base64-encoded image data. + Returns: + str: The model's response embedding. + """ + if not prompt and not image: + raise ValueError("Please provide either a text prompt, base64 image, or both as input") + + # Initialize the Amazon Bedrock runtime client + client = boto3.client(service_name="bedrock-runtime") + model_id = "amazon.titan-embed-image-v1" + + body = {"embeddingConfig": {"outputEmbeddingLength": output_embedding_length}} + + if prompt: + body["inputText"] = prompt + if image: + body["inputImage"] = image + + try: + response = client.invoke_model( + modelId=model_id, + body=json.dumps(body), + accept="application/json", + contentType="application/json" + ) + + # Process and return the response + result = json.loads(response.get("body").read()) + return result.get("embedding") + + except ClientError as err: + print(f"Couldn't invoke Titan embedding model. Error: {err.response['Error']['Message']}") + return None + + # Set embedding vector dimension + embedding_vector_dimension = 384 + + # Count the number of each type of item + item_counts = { + 'text': sum(1 for item in items if item['type'] == 'text'), + 'table': sum(1 for item in items if item['type'] == 'table'), + 'image': sum(1 for item in items if item['type'] == 'image'), + 'page': sum(1 for item in items if item['type'] == 'page') + } + + # Initialize counters + counters = dict.fromkeys(item_counts.keys(), 0) + + # Generate embeddings for all items + with tqdm( + total=len(items), + desc="Generating embeddings", + bar_format=( + "{l_bar}{bar}| {n_fmt}/{total_fmt} " + "[{elapsed}<{remaining}, {rate_fmt}{postfix}]" + ) + ) as pbar: + + for item in items: + item_type = item['type'] + counters[item_type] += 1 + + if item_type in ['text', 'table']: + # For text or table, use the formatted text representation + item['embedding'] = generate_multimodal_embeddings(prompt=item['text'],output_embedding_length=embedding_vector_dimension) + else: + # For images, use the base64-encoded image data + item['embedding'] = generate_multimodal_embeddings(image=item['image'], output_embedding_length=embedding_vector_dimension) + + # Update the progress bar + pbar.set_postfix_str(f"Text: {counters['text']}/{item_counts['text']}, Table: {counters['table']}/{item_counts['table']}, Image: {counters['image']}/{item_counts['image']}") + pbar.update(1) + + # All the embeddings + all_embeddings = np.array([item['embedding'] for item in items]) + + # Create FAISS Index + index = faiss.IndexFlatL2(embedding_vector_dimension) + + # Clear any pre-existing index + index.reset() + + # Add embeddings to the index + index.add(np.array(all_embeddings, dtype=np.float32)) + + # Generating RAG response with Amazon Nova + def invoke_nova_multimodal(prompt, matched_items): + """ + Invoke the Amazon Nova model. + """ + + + # Define your system prompt(s). + system_msg = [ + { "text": """You are a helpful assistant for question answering. + The text context is relevant information retrieved. + The provided image(s) are relevant information retrieved."""} + ] + + # Define one or more messages using the "user" and "assistant" roles. + message_content = [] + + for item in matched_items: + if item['type'] == 'text' or item['type'] == 'table': + message_content.append({"text": item['text']}) + else: + message_content.append({"image": { + "format": "png", + "source": {"bytes": item['image']}, + } + }) + + + # Configure the inference parameters. + inf_params = {"max_new_tokens": 300, + "top_p": 0.9, + "top_k": 20} + + # Define the final message list + message_list = [ + {"role": "user", "content": message_content} + ] + + # Adding the prompt to the message list + message_list.append({"role": "user", "content": [{"text": prompt}]}) + + native_request = { + "messages": message_list, + "system": system_msg, + "inferenceConfig": inf_params, + } + + # Initialize the Amazon Bedrock runtime client + model_id = "amazon.nova-pro-v1:0" + client = ChatBedrock(model_id=model_id) + + # Invoke the model and extract the response body. + response = client.invoke(json.dumps(native_request)) + model_response = response.content + + return model_response + + + # User Query + query = "Which optimizer was used when training the models?" + + # Generate embeddings for the query + query_embedding = generate_multimodal_embeddings(prompt=query,output_embedding_length=embedding_vector_dimension) + + # Search for the nearest neighbors in the vector database + distances, result = index.search(np.array(query_embedding, dtype=np.float32).reshape(1,-1), k=5) + + # Check the result (matched chunks) + result.flatten() + + # Retrieve the matched items + matched_items = [{k: v for k, v in items[index].items() if k != 'embedding'} for index in result.flatten()] + + # Generate RAG response with Amazon Nova + response = invoke_nova_multimodal(query, matched_items) + + display.Markdown(response) + + # List of queries (Replace with any query of your choice) + other_queries = ["How long were the base and big models trained?", + "Which optimizer was used when training the models?", + "What is the position-wise feed-forward neural network mentioned in the paper?", + "What is the BLEU score of the model in English to German translation (EN-DE)?", + "How is the scaled-dot-product attention is calculated?", + ] + + query = other_queries[0] # Replace with any query from the list above + + # Generate embeddings for the query + query_embedding = generate_multimodal_embeddings(prompt=query,output_embedding_length=embedding_vector_dimension) + + # Search for the nearest neighbors in the vector database + distances, result = index.search(np.array(query_embedding, dtype=np.float32).reshape(1,-1), k=5) + + # Retrieve the matched items + matched_items = [{k: v for k, v in items[index].items() if k != 'embedding'} for index in result.flatten()] + + # Generate RAG response with Amazon Nova + response = invoke_nova_multimodal(query, matched_items) + + # Display the response + display.Markdown(response) + + diff --git a/page_files/categorized/page3.py b/page_files/categorized/page3.py new file mode 100644 index 0000000000000000000000000000000000000000..f19e19530a84d748923ce1f3c9034c630dc58ad6 --- /dev/null +++ b/page_files/categorized/page3.py @@ -0,0 +1,62 @@ +import streamlit as st +import pandas as pd +import tabula +import pymupdf +import os +from tqdm import tqdm + + +def extract_tables_pymupdf(pdf_path): + """Extract tables using PyMuPDF (alternative method)""" + try: + doc = pymupdf.open(pdf_path) + all_tables = [] + + for page_num in range(len(doc)): + page = doc[page_num] + tables = page.find_tables() + + for table in tables: + # Extract table data + table_data = table.extract() + if table_data: + # Convert to DataFrame + df = pd.DataFrame(table_data[1:], columns=table_data[0]) + all_tables.append({ + 'page': page_num + 1, + 'dataframe': df + }) + + doc.close() + return all_tables + except Exception as e: + st.error(f"Error extracting tables with PyMuPDF: {e}") + return [] + +def main(): + st.title("PDF Table Extractor") + st.write("Upload a PDF to extract all tables") + + temp_path = "temp_uploaded.pdf" # Define here + + uploaded_file = st.file_uploader("Choose a PDF file", type="pdf") + + if uploaded_file is not None: + # Save uploaded file temporarily + with open(temp_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + # Using PyMuPDF + tables = extract_tables_pymupdf(temp_path) + + if tables: + st.success(f"Found {len(tables)} tables!") + + for idx, table_info in enumerate(tables): + st.subheader(f"Table {idx + 1} (Page {table_info['page']})") + df = table_info['dataframe'] + st.dataframe(df, use_container_width=True) + + # Clean up temp file + if os.path.exists(temp_path): + os.remove(temp_path) \ No newline at end of file diff --git a/page_files/categorized/page4.py b/page_files/categorized/page4.py new file mode 100644 index 0000000000000000000000000000000000000000..0a0047a756612f88123c6d8f63a4bf286b99804c --- /dev/null +++ b/page_files/categorized/page4.py @@ -0,0 +1,5 @@ +import streamlit as st +from pathlib import Path + +def main(): + st.write(f'# {Path(__file__).parent.name} - {Path(__file__).name}') \ No newline at end of file diff --git a/page_files/categorized/page5.py b/page_files/categorized/page5.py new file mode 100644 index 0000000000000000000000000000000000000000..0a0047a756612f88123c6d8f63a4bf286b99804c --- /dev/null +++ b/page_files/categorized/page5.py @@ -0,0 +1,5 @@ +import streamlit as st +from pathlib import Path + +def main(): + st.write(f'# {Path(__file__).parent.name} - {Path(__file__).name}') \ No newline at end of file diff --git a/page_files/categorized/propgraph.jpg b/page_files/categorized/propgraph.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a408c139e3004e8a820ac986285b96b55877418e Binary files /dev/null and b/page_files/categorized/propgraph.jpg differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..db3318f13e95645d575bd018ebd72a78cc07595d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +streamlit +pandas +altair +plotly +pillow +numpy +sqlalchemy +psycopg[binary] +python-dotenv +requests +PyMuPDF +opencv-python-headless +streamlit-card + diff --git a/test_db.py b/test_db.py new file mode 100644 index 0000000000000000000000000000000000000000..14c6cd7f26613307a3204c092c1187a0df2aeee8 --- /dev/null +++ b/test_db.py @@ -0,0 +1,4 @@ +from db import fetch_all + +rows = fetch_all("SELECT 1 AS test_value") +print(rows) \ No newline at end of file