from __future__ import annotations """ Analyze a human parts-based formula with Pino. This is a small utility for the user-supplied Matthew tea/woods formula. It: 1. Imports the parts-based JSON via pino.importer. 2. Runs the physical/IFRA verifier. 3. Runs the PIMT critic (cloud or local fallback). 4. Prints a compact human-readable summary. """ import json import logging import sys from pathlib import Path from pino.importer import import_formula from pino.inference_cloud import predict_cloud from pino.verifier import FragrancePipelineVerifier logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s") logger = logging.getLogger("pino.analyze_formula") def analyze(path: str | Path) -> None: imported = import_formula(path) if imported["unresolved"]: logger.warning("Unresolved materials: %s", imported["unresolved"]) formula = imported["formula"] print(f"Imported formula with {len(formula)} ingredients; sum = {sum(i['weight_fraction'] for i in formula):.6f}") verifier = FragrancePipelineVerifier( temperature_k=298.15, ambient_pressure_pa=101325.0, headspace_volume_m3=1e-3, liquid_volume_m3=1e-6, density_g_ml=0.9, surface_area_m2=1e-4, mass_transfer_coefficient=1e-4, ) result = verifier.run_sim(formula, duration_seconds=8 * 3600.0, interval_seconds=600.0) print(f"Physical/IFRA status: {result['status']}") print(f"IFRA passed: {result['ifra_report'].get('passed', False)}") if not result["ifra_report"].get("passed", False): print("IFRA violations:", json.dumps(result["ifra_report"].get("violations", []), indent=2)) if result["status"] in ("rejected", "depleted"): logger.error("Formula rejected by verifier: %s", result.get("message")) return formula_payload = { "formula": formula, "trajectory": result["trajectory"], "metadata": {"source": str(path), "analysis": True}, } prediction = predict_cloud(formula_payload) print("Prediction keys:", list(prediction.keys())) print("Objective shape:", len(prediction.get("objective", [[]])[0][0])) print("Subjective vector:", prediction.get("subjective", [[]])[0]) # Save full analysis output = { "imported": imported, "verification": result, "prediction": prediction, } out_path = Path("data/formulas/matthew_tea_woods_analysis.json") out_path.write_text(json.dumps(output, indent=2, ensure_ascii=False)) print(f"Full analysis written to {out_path}") if __name__ == "__main__": analyze(sys.argv[1] if len(sys.argv) > 1 else "data/formulas/matthew_tea_woods.json")