Spaces:
Running
Running
unknown
Claude Opus 5.5
Journal Formatter: footnotes, web references, books and reports, author marks, cleaner LaTeX
2eb3609 Download devtools/fmt_corpus.py from Rasheed/Test_AI_writing: direct link, hf CLI and curl.
- Browser
- Download file 15.3 kB
-
https://huggingface.co/spaces/Rasheed/Test_AI_writing/resolve/main/devtools/fmt_corpus.py
- Command line
-
hf download hf://spaces/Rasheed/Test_AI_writing/devtools/fmt_corpus.py
-
curl -L -o fmt_corpus.py https://huggingface.co/spaces/Rasheed/Test_AI_writing/resolve/main/devtools/fmt_corpus.py
15.3 kB
| """Test manuscripts for the Journal Formatter (realistic content, every feature the formatter must handle). | |
| python devtools/fmt_corpus.py [out_dir] -> numeric.docx, authoryear.docx, messy.docx | |
| Each manuscript records what it contains (FACTS), so devtools/fmt_audit.py can check nothing was lost.""" | |
| import io | |
| import os | |
| import sys | |
| from pathlib import Path | |
| from docx import Document | |
| from docx.enum.text import WD_ALIGN_PARAGRAPH | |
| from docx.oxml import parse_xml | |
| from docx.shared import Inches, Pt | |
| M_NS = 'xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"' | |
| W_NS = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"' | |
| TITLE = "Thermal Management of Lithium-Ion Battery Modules Using Phase Change Materials and Liquid Cooling" | |
| AUTHORS = [("Ravi Kumar", "1"), ("Anita Sharma", "2"), ("John Miller", "1,*")] | |
| AFFILS = ["1 Department of Mechanical Engineering, National Institute of Technology, Warangal 506004, India", | |
| "2 School of Energy Systems, University of Leeds, Leeds LS2 9JT, United Kingdom"] | |
| CORR = "* Corresponding author: john.miller@example.ac.uk" | |
| ABSTRACT = ("Effective thermal management is essential for the safety and service life of lithium-ion battery " | |
| "modules in electric vehicles. This study integrates a paraffin-based phase change material (PCM) with a " | |
| "serpentine liquid cooling plate and tests a 48 V module under 1C, 2C and 3C discharge. The hybrid system " | |
| "kept the maximum cell temperature below 41.2 °C at 3C, which is 7.4 °C lower than liquid cooling alone, " | |
| "and limited the temperature difference across the module to 2.1 °C. A coupled electro-thermal model " | |
| "reproduced the measurements within 0.8 °C. The results show that a thin PCM layer delays the onset of " | |
| "thermal runaway and reduces pump power by 23%.") | |
| KEYWORDS = ["lithium-ion battery", "phase change material", "liquid cooling", "thermal management", "electric vehicle"] | |
| REFS = [ # (key for author-year, numeric-style text, author-year text) | |
| ("Kumar et al., 2021", | |
| "R. Kumar, S. Patel, and L. Chen, “Hybrid cooling of battery packs,” Applied Thermal Engineering, vol. 185, p. 116389, 2021, doi: 10.1016/j.applthermaleng.2020.116389.", | |
| "Kumar, R., Patel, S., & Chen, L. (2021). Hybrid cooling of battery packs. Applied Thermal Engineering, 185, 116389. https://doi.org/10.1016/j.applthermaleng.2020.116389"), | |
| ("Wang and Li, 2019", | |
| "Y. Wang and H. Li, “Phase change materials for thermal storage: A review,” Energy Conversion and Management, vol. 198, pp. 111–129, 2019.", | |
| "Wang, Y., & Li, H. (2019). Phase change materials for thermal storage: A review. Energy Conversion and Management, 198, 111–129."), | |
| ("Bergman et al., 2011", | |
| "T. L. Bergman, A. S. Lavine, F. P. Incropera, and D. P. DeWitt, Fundamentals of Heat and Mass Transfer, 7th ed. Hoboken, NJ, USA: Wiley, 2011.", | |
| "Bergman, T. L., Lavine, A. S., Incropera, F. P., & DeWitt, D. P. (2011). Fundamentals of heat and mass transfer (7th ed.). Wiley."), | |
| ("Zhang et al., 2020", | |
| "X. Zhang, M. Ahmed, and P. Rossi, “Liquid cold plates for prismatic cells,” in Proc. IEEE Transportation Electrification Conf. (ITEC), Chicago, IL, USA, 2020, pp. 45–50.", | |
| "Zhang, X., Ahmed, M., & Rossi, P. (2020). Liquid cold plates for prismatic cells. In Proceedings of the IEEE Transportation Electrification Conference (pp. 45–50). IEEE."), | |
| ("Feng et al., 2018", | |
| "X. Feng, M. Ouyang, X. Liu, L. Lu, Y. Xia, and X. He, “Thermal runaway mechanism of lithium ion battery for electric vehicles: A review,” Energy Storage Materials, vol. 10, pp. 246–267, 2018.", | |
| "Feng, X., Ouyang, M., Liu, X., Lu, L., Xia, Y., & He, X. (2018). Thermal runaway mechanism of lithium ion battery for electric vehicles: A review. Energy Storage Materials, 10, 246–267."), | |
| ("Rao and Wang, 2011", | |
| "Z. Rao and S. Wang, “A review of power battery thermal energy management,” Renewable and Sustainable Energy Reviews, vol. 15, no. 9, pp. 4554–4571, 2011.", | |
| "Rao, Z., & Wang, S. (2011). A review of power battery thermal energy management. Renewable and Sustainable Energy Reviews, 15(9), 4554–4571."), | |
| ("ISO, 2018", | |
| "International Organization for Standardization, ISO 12405-4:2018 Electrically propelled road vehicles — Test specification for lithium-ion traction battery packs and systems, Geneva, 2018.", | |
| "International Organization for Standardization. (2018). ISO 12405-4:2018 Electrically propelled road vehicles — Test specification for lithium-ion traction battery packs and systems."), | |
| ("Battery University, 2023", | |
| "Battery University, “BU-410: Charging at high and low temperatures,” 2023. [Online]. Available: https://batteryuniversity.com/article/bu-410", | |
| "Battery University. (2023). BU-410: Charging at high and low temperatures. https://batteryuniversity.com/article/bu-410"), | |
| ] | |
| # body: (kind, text); {c:i,j} = citation of references i, j (1-based); {f:n} = figure n; {t:n} = table n | |
| BODY = [ | |
| ("h1", "Introduction"), | |
| ("p", "Electric vehicles depend on lithium-ion batteries whose performance and safety are strongly temperature " | |
| "dependent {c:1,2}. Above 45 °C, side reactions accelerate capacity fade, and at higher temperatures they " | |
| "can trigger thermal runaway {c:5}. Air cooling is simple but cannot remove the heat generated at high " | |
| "discharge rates {c:6}, which has motivated liquid cooling and phase change materials {c:1,2,4}."), | |
| ("p", "This paper makes three contributions:"), | |
| ("ul", "a hybrid PCM–liquid design that needs no additional pump capacity;"), | |
| ("ul", "experiments on a 48 V module at 1C, 2C and 3C following the ISO procedure {c:7};"), | |
| ("ul", "a validated electro-thermal model for design studies."), | |
| ("h1", "Materials and Methods"), | |
| ("h2", "Battery module and test bench"), | |
| ("p", "The module consists of twelve prismatic cells (50 Ah) connected in series. The test bench is shown in " | |
| "{f:1}, and the cell properties are listed in {t:1}. Temperatures were recorded with 24 T-type " | |
| "thermocouples at 1 Hz."), | |
| ("table", None), | |
| ("h2", "Electro-thermal model"), | |
| ("p", "The heat generated in each cell follows the simplified Bernardi equation {c:3}:"), | |
| ("eq", "1"), | |
| ("p", "where I is the current, U_oc the open-circuit voltage, U the terminal voltage and T the cell temperature. " | |
| "The PCM is modelled with the enthalpy method, and the latent heat of 180 kJ/kg is released over a " | |
| "melting range of 2 K{fn}."), | |
| ("ol", "Measure the open-circuit voltage at 10% state-of-charge steps."), | |
| ("ol", "Fit the entropic coefficient from the temperature sweep."), | |
| ("ol", "Validate the model against the 2C discharge."), | |
| ("h1", "Results"), | |
| ("p", "{f:1} compares the maximum cell temperature for the three cooling strategies. At 3C the hybrid system " | |
| "stays below 41.2 °C, 7.4 °C lower than liquid cooling alone, in line with earlier reports {c:1,4}. " | |
| "The temperature difference across the module never exceeded 2.1 °C (see {t:1})."), | |
| ("figure", None), | |
| ("h1", "Discussion"), | |
| ("p", "The PCM layer absorbs the heat peaks while the liquid loop removes the average load, so the pump can " | |
| "run at a lower flow rate. This agrees with the review by {c:2} and the runaway analysis in {c:5}. A " | |
| "limitation of this study is that ageing was not considered; manufacturer data suggest that charging " | |
| "at low temperature remains critical {c:8}."), | |
| ("h1", "Conclusions"), | |
| ("p", "A hybrid PCM–liquid cooling system kept a 48 V module below 41.2 °C at 3C and reduced pump power by 23%. " | |
| "Future work will address cycle ageing and the mass of the PCM layer."), | |
| ] | |
| FOOTNOTE = "The melting range was measured by differential scanning calorimetry at 5 K/min." | |
| TABLE = [["Property", "Value", "Unit"], | |
| ["Nominal capacity", "50", "Ah"], | |
| ["Nominal voltage", "3.7", "V"], | |
| ["Mass", "0.89", "kg"], | |
| ["Specific heat", "1100", "J/(kg·K)"]] | |
| TABLE_CAPTION = "Properties of the prismatic cells used in the module." | |
| FIG_CAPTION = "Maximum cell temperature at 3C for air, liquid and hybrid PCM–liquid cooling." | |
| FACTS = {"refs": len(REFS), "figures": 1, "tables": 1, "equations": 1, "footnotes": 1, | |
| "headings": sum(1 for k, _ in BODY if k in ("h1", "h2")), "list_items": sum(1 for k, _ in BODY if k in ("ul", "ol"))} | |
| def _figure_png(): | |
| from PIL import Image, ImageDraw | |
| img = Image.new("RGB", (900, 500), "white") | |
| d = ImageDraw.Draw(img) | |
| d.rectangle([60, 40, 860, 440], outline="black", width=2) | |
| for x, h, c in ((180, 300, "#c0392b"), (420, 220, "#2980b9"), (660, 150, "#27ae60")): | |
| d.rectangle([x, 440 - h, x + 120, 440], fill=c) | |
| buf = io.BytesIO() | |
| img.save(buf, "PNG") | |
| return buf.getvalue() | |
| def _omml_equation(): | |
| # Q = I (U_oc - U) - I T dU_oc/dT (display equation) | |
| sub = lambda base, s: (f"<m:sSub><m:e><m:r><m:t>{base}</m:t></m:r></m:e>" | |
| f"<m:sub><m:r><m:t>{s}</m:t></m:r></m:sub></m:sSub>") | |
| frac = ("<m:f><m:num><m:r><m:t>d</m:t></m:r>" + sub("U", "oc") + "</m:num>" | |
| "<m:den><m:r><m:t>dT</m:t></m:r></m:den></m:f>") | |
| return (f"<m:oMathPara {M_NS}><m:oMath><m:r><m:t>Q=I(</m:t></m:r>" + sub("U", "oc") | |
| + "<m:r><m:t>−U)−IT</m:t></m:r>" + frac + "</m:oMath></m:oMathPara>") | |
| def _add_footnote(doc, paragraph, text): | |
| """A real Word footnote (python-docx has no API for it): footnotes part + reference run.""" | |
| from docx.opc.constants import RELATIONSHIP_TYPE as RT | |
| from docx.opc.packuri import PackURI | |
| from docx.opc.part import Part | |
| part = doc.part | |
| xml = (f'<w:footnotes {W_NS}>' | |
| '<w:footnote w:type="separator" w:id="-1"><w:p><w:r><w:separator/></w:r></w:p></w:footnote>' | |
| '<w:footnote w:type="continuationSeparator" w:id="0"><w:p><w:r><w:continuationSeparator/></w:r></w:p></w:footnote>' | |
| f'<w:footnote w:id="1"><w:p><w:pPr><w:pStyle w:val="FootnoteText"/></w:pPr><w:r><w:rPr><w:vertAlign w:val="superscript"/></w:rPr><w:footnoteRef/></w:r><w:r><w:t xml:space="preserve"> {text}</w:t></w:r></w:p></w:footnote>' | |
| '</w:footnotes>') | |
| fpart = Part(PackURI("/word/footnotes.xml"), | |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", xml.encode(), part.package) | |
| part.relate_to(fpart, RT.FOOTNOTES) | |
| r = parse_xml(f'<w:r {W_NS}><w:rPr><w:vertAlign w:val="superscript"/></w:rPr><w:footnoteReference w:id="1"/></w:r>') | |
| paragraph._p.append(r) | |
| def build(kind="numeric"): | |
| """kind: numeric | authoryear | messy.""" | |
| doc = Document() | |
| st = doc.styles["Normal"] | |
| st.font.name, st.font.size = "Calibri", Pt(11) | |
| messy = kind == "messy" | |
| author_year = kind == "authoryear" | |
| def heading(text, level, n): | |
| if messy: # bold body text, typed numbers: no heading styles | |
| p = doc.add_paragraph() | |
| r = p.add_run(f"{n} {text}" if n else text) | |
| r.bold = True | |
| r.font.size = Pt(12 if level == 1 else 11) | |
| else: | |
| doc.add_heading(f"{n} {text}" if n else text, level) | |
| t = doc.add_paragraph(style="Title") | |
| t.add_run(TITLE) | |
| p = doc.add_paragraph() | |
| p.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| for i, (name, aff) in enumerate(AUTHORS): | |
| p.add_run(name) | |
| r = p.add_run(aff) | |
| r.font.superscript = True | |
| if i < len(AUTHORS) - 1: | |
| p.add_run(", ") | |
| for a in AFFILS: | |
| q = doc.add_paragraph() | |
| q.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| r = q.add_run(a[0]) | |
| r.font.superscript = True | |
| q.add_run(a[1:]) | |
| doc.add_paragraph(CORR) | |
| if messy: | |
| p = doc.add_paragraph() | |
| r = p.add_run("Abstract: ") | |
| r.bold = True | |
| p.add_run(ABSTRACT) | |
| else: | |
| doc.add_heading("Abstract", 1) | |
| doc.add_paragraph(ABSTRACT) | |
| p = doc.add_paragraph() | |
| r = p.add_run("Keywords: ") | |
| r.bold = True | |
| p.add_run((", " if messy else "; ").join(KEYWORDS)) | |
| n1 = n2 = 0 | |
| eq_done = fn_done = False | |
| fig_png = _figure_png() | |
| for k, text in BODY: | |
| if k == "h1": | |
| n1 += 1 | |
| n2 = 0 | |
| heading(text, 1, f"{n1}.") | |
| elif k == "h2": | |
| n2 += 1 | |
| heading(text, 2, f"{n1}.{n2}") | |
| elif k in ("p", "ul", "ol"): | |
| style = {"ul": "List Bullet", "ol": "List Number"}.get(k) | |
| par = doc.add_paragraph(style=style) if style else doc.add_paragraph() | |
| import re | |
| pos = 0 | |
| for m in re.finditer(r"\{(c|f|t):([\d,]+)\}|\{fn\}", text): | |
| par.add_run(text[pos:m.start()]) | |
| pos = m.end() | |
| if m.group(0) == "{fn}": | |
| _add_footnote(doc, par, FOOTNOTE) | |
| fn_done = True | |
| continue | |
| what, nums = m.group(1), [int(x) for x in m.group(2).split(",")] | |
| if what == "c": | |
| if author_year: | |
| par.add_run("(" + "; ".join(REFS[i - 1][0] for i in nums) + ")") | |
| else: | |
| par.add_run("[" + ", ".join(str(i) for i in nums) + "]") | |
| elif what == "f": | |
| par.add_run(("Fig. " if messy else "Figure ") + str(nums[0])) | |
| else: | |
| par.add_run("Table " + str(nums[0])) | |
| par.add_run(text[pos:]) | |
| elif k == "table": | |
| doc.add_paragraph(("Table 1: " if messy else "Table 1. ") + TABLE_CAPTION) | |
| tb = doc.add_table(rows=len(TABLE), cols=len(TABLE[0])) | |
| tb.style = "Table Grid" | |
| for i, row in enumerate(TABLE): | |
| for j, v in enumerate(row): | |
| tb.cell(i, j).text = v | |
| if i == 0: | |
| tb.cell(i, j).paragraphs[0].runs[0].bold = True | |
| elif k == "figure": | |
| doc.add_paragraph().add_run().add_picture(io.BytesIO(fig_png), width=Inches(5.5)) | |
| doc.add_paragraph(("Fig. 1: " if messy else "Figure 1. ") + FIG_CAPTION) | |
| elif k == "eq": | |
| p = doc.add_paragraph() | |
| p._p.append(parse_xml(_omml_equation())) | |
| p.add_run("\t(1)") | |
| eq_done = True | |
| heading("Acknowledgments", 1, "") | |
| doc.add_paragraph("The authors thank the Centre for Energy Storage for access to the test bench.") | |
| heading("References", 1, "") | |
| refs = sorted(REFS, key=lambda r: r[2]) if author_year else REFS | |
| for i, r in enumerate(refs, 1): | |
| doc.add_paragraph(r[2] if author_year else (f"{i}. " if messy else f"[{i}] ") + r[1]) | |
| assert eq_done and fn_done | |
| return doc | |
| def main(): | |
| out = Path(sys.argv[1] if len(sys.argv) > 1 else Path(__file__).resolve().parent / "logs" / "fmt_corpus") | |
| out.mkdir(parents=True, exist_ok=True) | |
| for kind in ("numeric", "authoryear", "messy"): | |
| build(kind).save(str(out / f"{kind}.docx")) | |
| print("written", out / f"{kind}.docx") | |
| if __name__ == "__main__": | |
| main() | |