From 971c6b4eb7305497646a6bd8405513b1855691ce Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:35:21 -0500 Subject: [PATCH 01/19] OpenLCA version update --- .virtual_documents/up_template.ipynb | 35 ++++++++++++++++++++++++++++ up_template.ipynb | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 .virtual_documents/up_template.ipynb diff --git a/.virtual_documents/up_template.ipynb b/.virtual_documents/up_template.ipynb new file mode 100644 index 0000000..e4c2178 --- /dev/null +++ b/.virtual_documents/up_template.ipynb @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + +from up_template import Interface +from up_template import get_logger + + + + + +if __name__ == '__main__': + logger = get_logger("INFO") + mc = Interface() + mc.run() diff --git a/up_template.ipynb b/up_template.ipynb index 32e21a4..588ee0f 100644 --- a/up_template.ipynb +++ b/up_template.ipynb @@ -292,7 +292,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.13.5" } }, "nbformat": 4, From 29307b1d7b6014fb40e04c3e908480d37bc0dc15 Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Wed, 21 Jan 2026 08:26:38 -0500 Subject: [PATCH 02/19] Update NetlOlcaReport.py --- up_template/NetlOlcaReport.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 8eb1dc5..9a858bd 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -398,10 +398,12 @@ def create_report_markdown(self): ![](data/diagram.png) ``` +### Scenarios + ### Input Flows {input_flows_md} -## Output Flows +### Output Flows {output_flows_md} ### Process Parameters @@ -762,6 +764,9 @@ def format_parameter_table(self, param_list): "The following table provides process and global parameter " "values and their associated uncertainty.\n\n" ) + #Open scrollable table + md_table += '
\n\n' + md_table += ( "| Scope | Name | Value | Uncertainty | Description |\n") md_table += ( @@ -812,6 +817,10 @@ def format_parameter_table(self, param_list): md_table += "| " + p_string + " |\n" md_formula += p_form + "\n\n" + #Close scrollable table + if num_params > 0: + md_table += "\n
\n" + # Add the formulas before the parameter table. md_table = md_formula + md_table From ad3c5d41e7a03395a5f84d30b66d216b41c13173 Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:10:02 -0500 Subject: [PATCH 03/19] NetlOlcaReport Updates - Added function to read scenarios from DS files (only reads specific file in data folder right now) - Edited Markdown block in create_markdown_report function to hard code some information --- up_template/NetlOlcaReport.py | 223 +++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 2 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 9a858bd..2a41c3d 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -340,6 +340,18 @@ def create_report_markdown(self): if len(self.sources) > 0: sources_md = "\n\n".join(self.sources) + # Scenario attribute table (from workbook in DATA_DIR) + try: + scenario_table_md = scenario_attribute_table( + workbook_filename="DS_Stage5_O_Natural_Gas_Combustion_2014.01.xlsx", + sheet_name="PS", + header_row_excel=6, + start_col_excel="D", + ) + except Exception as e: + logging.warning(f"Scenario attribute table could not be created: {e}") + scenario_table_md = "No scenario attributes available." + # Add the calculations content from the filled workbook calculations_md = self.calculations_content or "No calculations available." @@ -399,6 +411,7 @@ def create_report_markdown(self): ``` ### Scenarios +{scenario_table_md} ### Input Flows {input_flows_md} @@ -415,6 +428,47 @@ def create_report_markdown(self): ### Calculations {calculations_md} +### Impact Assessment Methodology +LCI Method +: Attributional + +Process Type +: Unit Process + +Modeling Constraints +: This model assumes the composition of natural gas and associated production emissions are dependent on geographic region, + and it accounts for these variations through the use of 30 regional parameters. In addition, five types of natural gas – + based on their extraction technologies – are considered: conventional natural gas, coalbed methane (CBM), shale gas, + tight gas, and associated gas. + +### Data Quality + +The following Data Quality Indicator (DQI) scores are assigned to this process +using the DQI Matrix (from NETL LCI&C Guideline Document, adapted from Weidema and Wenaes) + +
+ +| DQI Indicator | Score | Basis for Score | +|--------------|:-----:|----------------| +| Source Reliability | **3** | Verified data with many assumptions or non-verified data from a quality source; source quality guidelines not fully met | +| Completeness | **3** | Representative data from a sufficient number of sites, but over a less adequate period of time | +| Temporal Correlation | **2** | Less than 6 years difference between the data period and the study reference period | +| Geographical Correlation | **2** | Average data from a larger region including the area under study or data from a nearby region | +| Technological Correlation | **1** | Data from the technology, process, or material under study | + +
+ +### Data Source Information + +| Element | Description | +|--------|-------------| +| Data Completeness | All relevant flows captured | +| Data Selection | This model uses the Environmental Protection Agency (EPA)’s Greenhouse Gas Reporting Program (GHGRP) and Greenhouse Gas Inventory (GHGI) for the 2017 reporting year to account for venting and fugitive emissions from the natural gas supply chain. These data are representative of 2016 operations in the U.S. oil and gas sectors. Additional information on equipment counts, emission events, and natural gas throughput are provided by Drilling Info (DI Desktop) and the Energy Information Administration (EIA). | +| Data Treatment | The EPA’s 2017 GHGRP and GHGI account for most vented and fugitive emissions. The GHGRP double counts natural gas throughput; to mitigate this, throughput was scaled down by 19% for all basins except Permian, Gulf Coast, and Anadarko, which were not adjusted. For additional details on data treatment, see the referenced publication. | +| Sampling Procedure | For information on how data were collected, see the referenced publication. | +| Data Collection Period | 2017–2018 | +| Use Advice | None | + ## References {sources_md} @@ -1299,9 +1353,9 @@ def _fix_formula(p_name, f_txt, in_line=False): # Put between latex equation markers if in_line: - new_txt = "$%s%" % new_txt + new_txt = f"${new_txt}$" else: - new_txt = "$$\n%s\n$$" % new_txt + new_txt = f"$$\n{new_txt}\n$$" return new_txt @@ -1358,3 +1412,168 @@ def _fix_uncertainty(param): r_str = "%s (%s)" % (u_type, u_params) return r_str + +def scenario_attribute_table( + workbook_filename: str, + sheet_name: str = "PS", + *, + header_row_excel: int = 6, + start_col_excel: str = "D", + max_height_px: int = 600, + return_dataframe: bool = False, +): + """Build scenario attribute table from scenario header strings in an Excel sheet. + + Workbook must be located in DATA_DIR. Scenario names are assumed to be in the + header row (Excel row header_row_excel) starting at start_col_excel. + """ + workbook_path = os.path.join(DATA_DIR, workbook_filename) + if not os.path.isfile(workbook_path): + raise OSError(f"Workbook not found in DATA_DIR: {workbook_path}") + + # Convert Excel col letter(s) to 0-indexed integer + col_letter = str(start_col_excel).strip().upper() + if not col_letter or any(not ("A" <= c <= "Z") for c in col_letter): + raise ValueError(f"Invalid Excel column letter: {start_col_excel}") + + start_col_index = 0 + for c in col_letter: + start_col_index = start_col_index * 26 + (ord(c) - ord("A") + 1) + start_col_index -= 1 + + header_row_index = header_row_excel - 1 + scenario_source_df = pd.read_excel( + workbook_path, + sheet_name=sheet_name, + header=header_row_index, + engine="openpyxl", + ) + + scenario_header_candidates = list(scenario_source_df.columns[start_col_index:]) + scenario_names = [] + for header_val in scenario_header_candidates: + if pd.isna(header_val): + continue + header_text = re.sub(r"\s+", " ", str(header_val)).strip().strip(",") + if header_text.startswith("External Combustion Boilers") or header_text.startswith("Internal Combustion Engines"): + scenario_names.append(header_text) + + scenario_records = [] + for scenario_id, scenario_name in enumerate(scenario_names, start=1): + name = re.sub(r"\s+", " ", str(scenario_name)).strip().strip(",") + parts = [p.strip() for p in name.split(",") if p.strip()] + + tech = parts[0] if len(parts) > 0 else "NA" + sector = parts[1] if len(parts) > 1 else "NA" + fuel = parts[2] if len(parts) > 2 else "NA" + remainder = parts[3:] if len(parts) > 3 else [] + + regulatory_vintage = "NA" + size_class_mmbtu_hr = "NA" + firing_type = "NA" + operating_mode = "Standard" + + if "External Combustion Boilers" in tech: + technology_family = "External Combustion Boiler" + equipment_category = "Boiler" + + if "Pre-NSPS" in name: + regulatory_vintage = "Pre-NSPS" + elif "Post" in name and "NSPS" in name: + regulatory_vintage = "Post-NSPS" + + if any(r.lower() == "cogeneration" for r in remainder): + operating_mode = "Cogeneration" + equipment_subtype = "Cogeneration" + control_technology = remainder[-1] if remainder else "NA" + + scenario_records.append({ + "Scenario_ID": scenario_id, + "Scenario_Name": name, + "Technology_Family": technology_family, + "Sector": sector, + "Fuel": fuel, + "Equipment_Category": equipment_category, + "Equipment_Subtype": equipment_subtype, + "Size_Class_MMBtu_hr": "NA", + "Firing_Type": "NA", + "Control_Technology": control_technology, + "Regulatory_Vintage": regulatory_vintage, + "Operating_Mode": operating_mode, + }) + continue + + if any("Tangentially Fired Units" in r for r in remainder): + equipment_subtype = "Tangential" + firing_type = "Tangential" + else: + equipment_subtype = "Non-tangential" + firing_type = "Non-tangential" + joined = " ".join(remainder) + if re.search(r">\s*100", joined): + size_class_mmbtu_hr = ">100" + elif re.search(r"<\s*100", joined): + size_class_mmbtu_hr = "<100" + elif re.search(r"10\s*-\s*100", joined): + size_class_mmbtu_hr = "10-100" + elif re.search(r"<\s*10", joined): + size_class_mmbtu_hr = "<10" + + control_technology = remainder[-1] if remainder else "NA" + + else: + technology_family = "Internal Combustion Engine" + equipment_category = "Engine" + equipment_subtype = remainder[0] if remainder else "NA" + control_technology = remainder[-1] if remainder else "NA" + operating_mode = "Cogeneration" if ":Cogeneration" in equipment_subtype else "Standard" + regulatory_vintage = "NA" + size_class_mmbtu_hr = "NA" + firing_type = "NA" + + scenario_records.append({ + "Scenario_ID": scenario_id, + "Scenario_Name": name, + "Technology_Family": technology_family, + "Sector": sector, + "Fuel": fuel, + "Equipment_Category": equipment_category, + "Equipment_Subtype": equipment_subtype, + "Size_Class_MMBtu_hr": size_class_mmbtu_hr, + "Firing_Type": firing_type, + "Control_Technology": control_technology, + "Regulatory_Vintage": regulatory_vintage, + "Operating_Mode": operating_mode, + }) + + scenario_columns = [ + "Scenario_ID", + "Scenario_Name", + "Technology_Family", + "Sector", + "Fuel", + "Equipment_Category", + "Equipment_Subtype", + "Size_Class_MMBtu_hr", + "Firing_Type", + "Control_Technology", + "Regulatory_Vintage", + "Operating_Mode", + ] + scenario_table_df = pd.DataFrame(scenario_records) + for c in scenario_columns: + if c not in scenario_table_df.columns: + scenario_table_df[c] = "NA" + scenario_table_df = scenario_table_df[scenario_columns].fillna("NA") + + scenario_table_md = ( + f'
\n\n' + f'{scenario_table_df.to_markdown(index=False)}\n\n' + f'
\n' + ) + + if return_dataframe: + return scenario_table_df, scenario_table_md + return scenario_table_md + + From 77b5bb0489e5f73266915bf9db9b1b77b4340879 Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Tue, 27 Jan 2026 14:07:35 -0500 Subject: [PATCH 04/19] Update NetlOlcaReport.py --- up_template/NetlOlcaReport.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 2a41c3d..7758317 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -367,6 +367,8 @@ def create_report_markdown(self): ## Brief Description {process_desc} +### Technology Type + # Metadata {process_doc_md} @@ -469,6 +471,16 @@ def create_report_markdown(self): | Data Collection Period | 2017–2018 | | Use Advice | None | +# Unit Process Use Information +Use Advice +: + +Preferred Provider UPs +: + +Suggested Downstream UP +: + ## References {sources_md} @@ -485,6 +497,9 @@ def create_report_markdown(self): How to Cite This Document : TBA +Archived +: - + # Disclaimer/Terms of Use {self.disclaimer} """ From 78c8246208cc2592a22330fa8c80c8682c831472 Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:23:32 -0500 Subject: [PATCH 05/19] Report template functions formatted --- up_template/NetlOlcaReport.py | 135 ++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 62 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 7758317..454d113 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -372,28 +372,6 @@ def create_report_markdown(self): # Metadata {process_doc_md} -## Relevant Flows Included: - -Releases to Air -: - [ ] Greenhouse Gases - - [ ] Criteria Air Pollutants - - [ ] Other - -Releases to Water -: - [ ] Inorganic Emissions - - [ ] Organic Emissions - - [ ] Other - -Releases to Soil -: - [ ] Inorganic Emissions - - [ ] Organic Emissions - - [ ] Other - -Water Usage -: - [ ] Water Demand - - [ ] Water Consumption - - # Process Description ## Goal & Scope @@ -404,25 +382,29 @@ def create_report_markdown(self): ## Methods -### Block Flow Diagram -Link to your block flow diagram. -For example: +### System Boundary Diagram -```sh ![](data/diagram.png) -``` ### Scenarios +
{scenario_table_md} +
### Input Flows +
{input_flows_md} +
### Output Flows +
{output_flows_md} +
### Process Parameters +
{param_table_md} +
### Allocation {allocation_md} @@ -469,11 +451,10 @@ def create_report_markdown(self): | Data Treatment | The EPA’s 2017 GHGRP and GHGI account for most vented and fugitive emissions. The GHGRP double counts natural gas throughput; to mitigate this, throughput was scaled down by 19% for all basins except Permian, Gulf Coast, and Anadarko, which were not adjusted. For additional details on data treatment, see the referenced publication. | | Sampling Procedure | For information on how data were collected, see the referenced publication. | | Data Collection Period | 2017–2018 | -| Use Advice | None | -# Unit Process Use Information +## Unit Process Use Information Use Advice -: +: Preferred Provider UPs : @@ -495,10 +476,10 @@ def create_report_markdown(self): : {version_number} How to Cite This Document -: TBA +: National Energy Technology Laboratory (NETL), Combustion of Natural Gas, 11/01/2014, https://edx.netl.doe.gov/dataset/lca-up-combustion-of-natural-gas, OSTI ID: 1509357 Archived -: - +: False # Disclaimer/Terms of Use {self.disclaimer} @@ -516,73 +497,103 @@ def empty_template(self): """ empty_rp = f"""# Overview + ## Process Name ## Reference Flow ## Brief Description -# Metadata - -## Relevant Flows Included: - -Releases to Air -: - [ ] Greenhouse Gases - - [ ] Criteria Air Pollutants - - [ ] Other - -Releases to Water -: - [ ] Inorganic Emissions - - [ ] Organic Emissions - - [ ] Other - -Releases to Soil -: - [ ] Inorganic Emissions - - [ ] Organic Emissions - - [ ] Other - -Water Usage -: - [ ] Water Demand - - [ ] Water Consumption +### Technology Type +# Metadata # Process Description ## Goal & Scope - ## Boundary & Description - ## Methods -### Block Flow Diagram +### System Boundary Diagram + +### Scenarios ### Input Flows ### Output Flows ### Process Parameters - ### Allocation ### Calculations +### Impact Assessment Methodology +LCI Method +: + +Process Type +: + +Modeling Constraints +: + +### Data Quality + +Data Quality Methodology: + +
+ +| DQI Indicator | Score | Basis for Score | +|--------------|:-----:|----------------| +| Source Reliability | | | +| Completeness | | | +| Temporal Correlation | | | +| Geographical Correlation | | | +| Technological Correlation | | | + +
+ +### Data Source Information + +| Element | Description | +|--------|-------------| +| Data Completeness | | +| Data Selection | | +| Data Treatment | | +| Sampling Procedure | | +| Data Collection Period | | + +## Unit Process Use Information +Use Advice +: + +Preferred Provider UPs +: + +Suggested Downstream UP +: + ## References + # Document Control Information Date Created -: TBA +: Point of Contact -: TBA +: Revision History -: TBA +: How to Cite This Document -: TBA +: + +Archived +: # Disclaimer/Terms of Use {self.disclaimer} From 08ea626a56051f8a6f1889cdd62a75a4209371ec Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:36:50 -0500 Subject: [PATCH 06/19] Report markdown edits Edited create_report_markdown and empty_markdown functions. --- up_template/NetlOlcaReport.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 454d113..5eaf212 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -387,23 +387,35 @@ def create_report_markdown(self): ![](data/diagram.png) ### Scenarios +
+ {scenario_table_md} +
### Input Flows +
+ {input_flows_md} +
### Output Flows +
+ {output_flows_md} +
### Process Parameters +
+ {param_table_md} +
### Allocation @@ -548,11 +560,11 @@ def empty_template(self): | DQI Indicator | Score | Basis for Score | |--------------|:-----:|----------------| -| Source Reliability | | | -| Completeness | | | -| Temporal Correlation | | | -| Geographical Correlation | | | -| Technological Correlation | | | +| | | | +| | | | +| | | | +| | | | +| | | | From 06ad077f9c7fe5e294a458b7e15ffd584cf57d71 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 19 Feb 2026 08:59:58 -0500 Subject: [PATCH 07/19] remove .virtual_documents from repo --- .gitignore | 1 + .virtual_documents/up_template.ipynb | 35 ---------------------------- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 .virtual_documents/up_template.ipynb diff --git a/.gitignore b/.gitignore index 786ad64..ff4c48b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .vscode .DS_Store .ipynb_checkpoints +.virtual_documents *.html __pycache__ diff --git a/.virtual_documents/up_template.ipynb b/.virtual_documents/up_template.ipynb deleted file mode 100644 index e4c2178..0000000 --- a/.virtual_documents/up_template.ipynb +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -from up_template import Interface -from up_template import get_logger - - - - - -if __name__ == '__main__': - logger = get_logger("INFO") - mc = Interface() - mc.run() From 507ad3a56f217fb2e653618e6df0b9c5386778f7 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 19 Feb 2026 14:15:42 -0500 Subject: [PATCH 08/19] stop tracking data and output on repo --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ff4c48b..9e44683 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,9 @@ up_template.egg-info/* dockers/*/Dockerfile .env +# Ignore data and outputs on repo +data +output + # Keep the HTML resources with the repo !resources/*.html \ No newline at end of file From 57da64645be6a88e3ee28f5fc804bbcfec3cb3f7 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 19 Feb 2026 14:15:53 -0500 Subject: [PATCH 09/19] add missing openpyxl --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 79fbdf5..50e04e8 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ packages=['up_template'], install_requires=[ "netlolca @ git+https://github.com/NETL-RIC/netlolca#egg=netlolca", + "openpyxl", "pandas", "sympy", "tabulate", From 581b0f2568ce07d7d67df7c1ec0e3a75ee6823c5 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 19 Feb 2026 14:19:26 -0500 Subject: [PATCH 10/19] new styles, fixes, and improvements Move scrollable table styles to CSS file. For test case, read the external parameter table (there's a separate function that created it and just hard-code reading it during fetch data). Clean up some of the scrolling divs and try to link them all to the CSS for convenience. New style for parameter table with fixed first column. Still to reorganize the sections. --- resources/before_body.html | 3 +- resources/styles.css | 16 +++++ up_template/NetlOlcaReport.py | 132 ++++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 37 deletions(-) create mode 100644 resources/styles.css diff --git a/resources/before_body.html b/resources/before_body.html index bfa5b78..7bbba8b 100644 --- a/resources/before_body.html +++ b/resources/before_body.html @@ -6,8 +6,9 @@

NETL Life Cycle Inventory Data Process Document File

diff --git a/resources/styles.css b/resources/styles.css new file mode 100644 index 0000000..b2621f2 --- /dev/null +++ b/resources/styles.css @@ -0,0 +1,16 @@ +.table-wrapper { + overflow-x: scroll; + max-height:900px; +} + +.horizontal-scroll-except-first-column { + overflow-x: auto; +} + +.horizontal-scroll-except-first-column > table > * > tr > th:first-child, +.horizontal-scroll-except-first-column > table > * > tr > td:first-child { + position: sticky; + left: 0; + z-index: 2; + background-color: white; +} diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 5eaf212..7dc20a2 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -12,8 +12,22 @@ This module is a key part of the NETL Life Cycle Analysis (LCA) toolkit, designed for streamlined analysis and report generation. +Examples: + +>>> from up_template import NetlOlcaReport +>>> from netlolca import NetlOlca +>>> n = NetlOlca() # initialize data manager +>>> n.connect() # establish with IPC Server +>>> n.read() # read openLCA database +>>> r = NetlOlcaReport(n) # creates 'output' folder +>>> ps_id = r.netlolca.get_spec_ids( # find 1st product system UUID +... r.netlolca.get_spec_class("Product system") +... )[0] +>>> r.fetch_data(ps_id) # automated data scraping +>>> r.convert_to_html() # create .md; render to .html + Last Edited: - 2024-11-01 + 2026-02-19 """ __all__ = [ "NetlOlcaReport", @@ -223,6 +237,8 @@ def convert(self, from_file, to_file): '--section-divs', '--css', 'https://cdnjs.cloudflare.com/ajax/libs/concrete.css/3.0.0/concrete.min.css', + '--css', + 'resources/styles.css', '--mathjax', '--include-before-body', 'resources/before_body.html', @@ -388,15 +404,11 @@ def create_report_markdown(self): ### Scenarios -
- {scenario_table_md} -
- ### Input Flows -
+
{input_flows_md} @@ -404,7 +416,7 @@ def create_report_markdown(self): ### Output Flows -
+
{output_flows_md} @@ -412,12 +424,8 @@ def create_report_markdown(self): ### Process Parameters -
- {param_table_md} -
- ### Allocation {allocation_md} @@ -432,9 +440,9 @@ def create_report_markdown(self): : Unit Process Modeling Constraints -: This model assumes the composition of natural gas and associated production emissions are dependent on geographic region, - and it accounts for these variations through the use of 30 regional parameters. In addition, five types of natural gas – - based on their extraction technologies – are considered: conventional natural gas, coalbed methane (CBM), shale gas, +: This model assumes the composition of natural gas and associated production emissions are dependent on geographic region, + and it accounts for these variations through the use of 30 regional parameters. In addition, five types of natural gas – + based on their extraction technologies – are considered: conventional natural gas, coalbed methane (CBM), shale gas, tight gas, and associated gas. ### Data Quality @@ -442,7 +450,7 @@ def create_report_markdown(self): The following Data Quality Indicator (DQI) scores are assigned to this process using the DQI Matrix (from NETL LCI&C Guideline Document, adapted from Weidema and Wenaes) -
+
| DQI Indicator | Score | Basis for Score | |--------------|:-----:|----------------| @@ -469,10 +477,10 @@ def create_report_markdown(self): : Preferred Provider UPs -: +: Suggested Downstream UP -: +: ## References {sources_md} @@ -544,17 +552,17 @@ def empty_template(self): ### Impact Assessment Methodology LCI Method -: +: Process Type -: +: Modeling Constraints -: +: ### Data Quality -Data Quality Methodology: +Data Quality Methodology:
@@ -583,29 +591,29 @@ def empty_template(self): : Preferred Provider UPs -: +: Suggested Downstream UP -: +: ## References # Document Control Information Date Created -: +: Point of Contact -: +: Revision History -: +: How to Cite This Document -: +: Archived -: +: # Disclaimer/Terms of Use {self.disclaimer} @@ -652,8 +660,13 @@ def fetch_data(self, uuid=None): self.project_doc = self.format_project_doc(raw_process_doc) self.poc = self.format_poc(raw_process_doc) - param_list = self.netlolca.find_process_parameters(uuid) - self.param_table = self.format_parameter_table(param_list) + # NEW: EY25 test case uses the supplemental MD table [260219;TWD] + # param_list = self.netlolca.find_process_parameters(uuid) + # self.param_table = self.format_parameter_table(param_list) + self.param_table = '
' + self.param_table += '\n\n' + self.param_table += read_markdown_table("up_test_parameters.txt") + self.param_table += "\n\n
" sources = self.netlolca.get_sources(uuid, False) self.sources = [self.format_source(x) for x in sources] @@ -779,8 +792,11 @@ def format_flows(self, flows): comp = flows['category'][i] dq = flows['dq'][i] name = flows['name'][i] - # HOTFIX: rounds to 3 significant figures in scientific not. - quantity = "%0.3E" % flows['amount'][i] + # HOTFIX: for scenarios, use formula if available [260219; TWD] + quantity = flows['amountFormula'][i] + if quantity is None: + # HOTFIX: round to 3 sig figs in scientific notation + quantity = "%0.3E" % flows['amount'][i] unit = flows['unit'][i] flows_markdown += ( f"| {comp} | {name} | {quantity} | {unit} | {dq} |\n" @@ -857,8 +873,8 @@ def format_parameter_table(self, param_list): "values and their associated uncertainty.\n\n" ) #Open scrollable table - md_table += '
\n\n' - + md_table += '
\n\n' + md_table += ( "| Scope | Name | Value | Uncertainty | Description |\n") md_table += ( @@ -909,10 +925,10 @@ def format_parameter_table(self, param_list): md_table += "| " + p_string + " |\n" md_formula += p_form + "\n\n" - #Close scrollable table + # Close scrollable table if num_params > 0: md_table += "\n
\n" - + # Add the formulas before the parameter table. md_table = md_formula + md_table @@ -1451,6 +1467,35 @@ def _fix_uncertainty(param): return r_str + +def read_markdown_table(md_file): + """Helper function to read a markdown table from a file. + + Parameters + ---------- + md_file : str + Markdown file name. + Note: file must be located in the ``DATA_DIR`` directory. + + Returns + ------- + str + Markdown table as a Python string. + + Raises + ------ + OSError + If the file does not exist in the ``DATA_DIR``. + """ + file_path = os.path.join(DATA_DIR, md_file) + if not os.path.isfile(file_path): + raise OSError("Failed to find %s in %s!" % (md_file, DATA_DIR)) + with open(file_path, 'r') as f: + my_data = f.readlines() # newline character is in the string + my_str = "".join(my_data) + return my_str + + def scenario_attribute_table( workbook_filename: str, sheet_name: str = "PS", @@ -1615,3 +1660,18 @@ def scenario_attribute_table( return scenario_table_md +# +# SANDBOX +# +if __name__ == '__main__': + from up_template import NetlOlcaReport + from netlolca import NetlOlca + n = NetlOlca() # initialize data manager + n.connect() # establish with IPC Server + n.read() # read openLCA database + r = NetlOlcaReport(n) # creates 'output' folder + ps_id = r.netlolca.get_spec_ids( # find 1st product system UUID + r.netlolca.get_spec_class("Product system") + )[0] + r.fetch_data(ps_id) # automated data scraping + r.convert_to_html() # create .md; render to .html From 52f99d7261e9bdbb93bca4397c118a2ed2bc1c21 Mon Sep 17 00:00:00 2001 From: Pri <98677561+pricarious@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:13:31 -0500 Subject: [PATCH 11/19] Updates to empty template format Including some instructions in a few sections --- up_template/NetlOlcaReport.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 5eaf212..77e7eff 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -512,19 +512,38 @@ def empty_template(self): ## Process Name +Format: "Base name; treatment received, production route(s), standard(s) fulfilled; production or consumption type, location type; quantitative flow properties" + ## Reference Flow ## Brief Description -### Technology Type +High-level process summary that communicates the basic technology and what is included in the boundary. -# Metadata +- Functional Unit: 1 unit of quantitative reference flow +- Co-products: List names, if applicable; otherwise, N/A +- Default co-product approach: If applicable; otherwise, N/A. Examples include system expansion, mass allocation, etc. - list what is used and if there are any restrictions. +- Data source: Abbreviated citation: (Author, Year). Name should align with source in Documentation tab. -# Process Description +State the intended use. -## Goal & Scope +### Technology -## Boundary & Description +High level technology categorization, such as natural gas, bio fuels, etc. + +#### Description + +Enter a brief technology description of the model, answering question such as: “What technology is modeled?” or, “Are there any modifications?” + +## Metadata + +#### Goal & Scope + +Define the purpose, and boundaries of the unit process, specifying what is included or excluded in the inventory. + +#### Boundary & Description + +Describes the specific cutoff points that determine which inputs, outputs, and life cycle stages are included or excluded in the inventory. ## Methods From 497c37ecfedbfe9d990e95d514d82c4f075d8ee1 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Wed, 4 Mar 2026 15:00:40 -0500 Subject: [PATCH 12/19] style changes for HTML make default tables have a static header row; set a max table height for the horizontal scroll table with static first column; make definition terms bold --- resources/styles.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/resources/styles.css b/resources/styles.css index b2621f2..349408b 100644 --- a/resources/styles.css +++ b/resources/styles.css @@ -3,8 +3,16 @@ max-height:900px; } +.table-wrapper > table > thead > tr { + position: sticky; + top: 0; + background-color: white; +} + + .horizontal-scroll-except-first-column { overflow-x: auto; + max-height:900px; } .horizontal-scroll-except-first-column > table > * > tr > th:first-child, @@ -14,3 +22,7 @@ z-index: 2; background-color: white; } + +dt { + font-weight: bold; +} \ No newline at end of file From 0c2ea78c707f4d2ce93a8fd35826a8d4c882b6a5 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Wed, 4 Mar 2026 15:49:33 -0500 Subject: [PATCH 13/19] update empty template format w/ descriptions; addresses #8 --- up_template/NetlOlcaReport.py | 130 +++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 48 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 058709e..5da441f 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -27,7 +27,7 @@ >>> r.convert_to_html() # create .md; render to .html Last Edited: - 2026-02-19 + 2026-03-04 """ __all__ = [ "NetlOlcaReport", @@ -517,73 +517,91 @@ def empty_template(self): """ empty_rp = f"""# Overview +_The following template has instructions included under each section in italics._ ## Process Name -Format: "Base name; treatment received, production route(s), standard(s) fulfilled; production or consumption type, location type; quantitative flow properties" +_Process naming format should be based on the following: "Base name; treatment received, production route(s), standard(s) fulfilled; production or consumption type, location type; quantitative flow properties"_ ## Reference Flow +_Copy and paste the quantitative reference flow for the process including the flow amount, units, and name._ + ## Brief Description -High-level process summary that communicates the basic technology and what is included in the boundary. +_High-level process summary that communicates the basic technology and what is included in the boundary; see example below._ -- Functional Unit: 1 unit of quantitative reference flow -- Co-products: List names, if applicable; otherwise, N/A -- Default co-product approach: If applicable; otherwise, N/A. Examples include system expansion, mass allocation, etc. - list what is used and if there are any restrictions. -- Data source: Abbreviated citation: (Author, Year). Name should align with source in Documentation tab. +- Process Type: _State whether this is a Unit Process or System Process_ +- Inventory Method: _State whether this is Attributional or Consequential_ +- Technology Type: _High level technology categorization, such as natural gas, or bio fuels_ +- Unit Process Boundary: Gate-to-Gate (GG) +- Co-products: _List names, if applicable; otherwise, N/A_ +- Co-product allocation: _If applicable; otherwise, N/A. Examples include system expansion and mass allocation; list what is used and if there are any restrictions._ +- Scenarios: _Yes or no (e.g., link to [Scenarios](#scenarios) section)_ +- Location: _Geographic region associated with the unit process_ +- Valid From: _Year or date format_ +- Valid Until: _Year or date format_ +- Unit Process Creation Date: _May be different from report date_ +- Data Quantity Score: _The process DQI score (e.g., link to [Data Quality](#data-quality) for explanation of scoring)_ +- Data Completeness: _Link to [Data Source Information](#data-source-information) for more information_ -State the intended use. +_Conclude with the stated intended use._ -### Technology +# Process Description -High level technology categorization, such as natural gas, bio fuels, etc. +## Goal & Scope -#### Description +_Define the purpose, and boundaries of the unit process, specifying what is included or excluded in the inventory._ -Enter a brief technology description of the model, answering question such as: “What technology is modeled?” or, “Are there any modifications?” +## Process Boundary -## Metadata +_Describes the specific cutoff points that determine which inputs, outputs, and life cycle stages are included or excluded in the inventory._ -#### Goal & Scope +_Also, include a graphic of the box diagram for your process._ -Define the purpose, and boundaries of the unit process, specifying what is included or excluded in the inventory. +## Input Flows -#### Boundary & Description +_Provide an introduction (e.g., "The table below provides a list of resource flows for this process.") and provide a table of resources with columns: 'Compartment', 'Flow Name', 'Quantity', 'Unit', and 'DQI' (automated in Python)._ -Describes the specific cutoff points that determine which inputs, outputs, and life cycle stages are included or excluded in the inventory. +## Output Flows -## Methods +_Provide an introduction (e.g., "The table below provides a list of emission flows for this process.") and provide a table of resources with columns: 'Compartment', 'Flow Name', 'Quantity', 'Unit', and 'DQI' (automated in Python)._ -### System Boundary Diagram +## Preferred Providers -### Scenarios +_Include a process or description of upstream providers for each of the product flows identified in the input table._ -### Input Flows +## Suggested Downstream Uses -### Output Flows +_Include a process or description of process, models, or domains where the use of this unit process may be applicable._ -### Process Parameters +# Methods -### Allocation +## Scenarios -### Calculations +_For natural gas unit processes, there are often scenarios that may be described in more detail here._ -### Impact Assessment Methodology -LCI Method -: +## Allocation -Process Type -: +_State whether any allocation methods were used across co-products or by-production identified in the outputs table._ -Modeling Constraints -: -### Data Quality +## Calculations and Supplements + +_Include links and descriptions to additional resources that were utilized in creating this report and how they were utilized._ +_This includes, but is not limited to Excel workbooks, Python scripts, Jupyter notebooks, and other data files._ -Data Quality Methodology: +## Modeling Assumptions -
+_List any assumptions and known limitations to the utility or applicability of this unit process._ + +# Data + +## Data Quality + +_Provide an overview of the data quality index utilized for this process, and include a table of its scores (see below for example empty table)._ + +
| DQI Indicator | Score | Basis for Score | |--------------|:-----:|----------------| @@ -595,7 +613,11 @@ def empty_template(self):
-### Data Source Information +## Data Source Information + +_Additional data source metadata is included here (e.g., Data Completeness, Data Selection, Data Treatment, Sampling Procedure, and Data Collection Period), which should all be automated from openLCA (see below for empty table example)._ + +
| Element | Description | |--------|-------------| @@ -605,36 +627,48 @@ def empty_template(self): | Sampling Procedure | | | Data Collection Period | | -## Unit Process Use Information +
+ + +## References + +_List the references cited in this report following a standard style (e.g., APA or IEEE)._ + +# Use Information + Use Advice -: +: _Describe the intended application and any additional uses._ -Preferred Provider UPs -: +Access and Restrictions +: Neither the U.S. Department of Energy (DOE) National Energy Technology Laboratory (NETL) nor any person acting on behalf of these organizations: -Suggested Downstream UP -: + A. Makes any warranty or representation, express or implied, with respect to the accuracy, completeness, or usefulness of the information contained in this document, or that the use of any information, apparatus, method, or process disclosed in this document may not infringe on privately owned rights; or -## References + B. Assumes any liability with this report as to its use, or damages resulting from the use of any information, apparatus, method, or process disclosed in this document. + Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise, does not necessarily constitute or imply its endorsement, recommendation, or favoring by NETL. The views and opinions of the authors expressed herein do not necessarily state or reflect those of NETL. # Document Control Information + Date Created -: +: _The date when the report was finalized._ Point of Contact -: +: _Name and email address._ Revision History -: +: _Version number. If other than 1.0, consider including a list of changes._ How to Cite This Document -: +: _Recommended citation (full text)._ Archived -: +: _True/False (Yes/No) to indicate the active (false) or superseded (true) status._ # Disclaimer/Terms of Use + +_Standard federal disclaimer (see default language below)._ + {self.disclaimer} """ From a8e5c37c64196e1bceb645ffe2e2cc97757a1030 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 5 Mar 2026 10:15:33 -0500 Subject: [PATCH 14/19] update automated report generator w/ new layout; addresses #8 --- up_template/NetlOlcaReport.py | 139 +++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index 5da441f..f178b7c 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -27,7 +27,7 @@ >>> r.convert_to_html() # create .md; render to .html Last Edited: - 2026-03-04 + 2026-03-05 """ __all__ = [ "NetlOlcaReport", @@ -330,6 +330,9 @@ def create_report_markdown(self): Notes ----- + This is a semi-automated report for the natural gas combustion unit + process. + Class attributes are updated with fetched data, see :func:`fetch_data`. Returns @@ -356,7 +359,7 @@ def create_report_markdown(self): if len(self.sources) > 0: sources_md = "\n\n".join(self.sources) - # Scenario attribute table (from workbook in DATA_DIR) + # Issue #8. Scenario attribute table (from workbook in DATA_DIR). try: scenario_table_md = scenario_attribute_table( workbook_filename="DS_Stage5_O_Natural_Gas_Combustion_2014.01.xlsx", @@ -371,7 +374,7 @@ def create_report_markdown(self): # Add the calculations content from the filled workbook calculations_md = self.calculations_content or "No calculations available." - # Create markdown report + # Issue #8. Create markdown report (this has hard-coded values). report_md = f"""# Overview ## Process Name @@ -381,33 +384,23 @@ def create_report_markdown(self): {ref_flow} ## Brief Description -{process_desc} - -### Technology Type - -# Metadata {process_doc_md} + # Process Description ## Goal & Scope -{project_doc_md} +{process_desc} -## Boundary & Description +## Process Boundary {boundary_desc} -## Methods - -### System Boundary Diagram - ![](data/diagram.png) -### Scenarios - -{scenario_table_md} - ### Input Flows +The table below provides a list of resource flows for this process. +
{input_flows_md} @@ -416,87 +409,113 @@ def create_report_markdown(self): ### Output Flows +The table below provides a list of emission flows for this process. +
{output_flows_md}
-### Process Parameters +## Preferred Providers + +_For each input product flow, add a statement as to its potential provider process(es)._ + +## Suggested Downstream Uses + +_Based on the quantitative reference flow, add a statement as to what processes may use this flow as a resource (if any)._ + + +# Methods + +## Scenarios + +The following table describes the scenarios used in this process. + +{scenario_table_md} + +## Process Parameters + +The following table provides parameter quantities for each of the scenarios defined [above](#scenarios). +These parameters represent the emissions found in the [output flow table](#output-flows). {param_table_md} -### Allocation +## Allocation {allocation_md} -### Calculations -{calculations_md} +## Calculations and Supplements + +This report utilized the following supplemental files: + +- .xlsx + * _Include any workbooks used._ +- .py + * _Include any Python scripts (or Jupyter notebooks) used._ +- .zolca + * _Include the openLCA .zolca (or JSON-LD)._ -### Impact Assessment Methodology -LCI Method -: Attributional -Process Type -: Unit Process +## Modeling Assumptions + +- _List the modeling assumptions, which may be found in "Modeling constants" section for process documentation in openLCA._ -Modeling Constraints -: This model assumes the composition of natural gas and associated production emissions are dependent on geographic region, - and it accounts for these variations through the use of 30 regional parameters. In addition, five types of natural gas – - based on their extraction technologies – are considered: conventional natural gas, coalbed methane (CBM), shale gas, - tight gas, and associated gas. +# Data -### Data Quality +## Data Quality The following Data Quality Indicator (DQI) scores are assigned to this process -using the DQI Matrix (from NETL LCI&C Guideline Document, adapted from Weidema and Wenaes) +using the DQI Matrix.
| DQI Indicator | Score | Basis for Score | |--------------|:-----:|----------------| -| Source Reliability | **3** | Verified data with many assumptions or non-verified data from a quality source; source quality guidelines not fully met | -| Completeness | **3** | Representative data from a sufficient number of sites, but over a less adequate period of time | -| Temporal Correlation | **2** | Less than 6 years difference between the data period and the study reference period | -| Geographical Correlation | **2** | Average data from a larger region including the area under study or data from a nearby region | -| Technological Correlation | **1** | Data from the technology, process, or material under study | +| Source Reliability | **x** | _description_ | +| Completeness | **x** | _description_ | +| Temporal Correlation | **x** | _description_ | +| Geographical Correlation | **x** | _description_ | +| Technological Correlation | **x** | _description_ |
-### Data Source Information +## Data Source Information + +_Copy the values from the named fields below from openLCA's process documentation into the table._ | Element | Description | |--------|-------------| -| Data Completeness | All relevant flows captured | -| Data Selection | This model uses the Environmental Protection Agency (EPA)’s Greenhouse Gas Reporting Program (GHGRP) and Greenhouse Gas Inventory (GHGI) for the 2017 reporting year to account for venting and fugitive emissions from the natural gas supply chain. These data are representative of 2016 operations in the U.S. oil and gas sectors. Additional information on equipment counts, emission events, and natural gas throughput are provided by Drilling Info (DI Desktop) and the Energy Information Administration (EIA). | -| Data Treatment | The EPA’s 2017 GHGRP and GHGI account for most vented and fugitive emissions. The GHGRP double counts natural gas throughput; to mitigate this, throughput was scaled down by 19% for all basins except Permian, Gulf Coast, and Anadarko, which were not adjusted. For additional details on data treatment, see the referenced publication. | -| Sampling Procedure | For information on how data were collected, see the referenced publication. | -| Data Collection Period | 2017–2018 | +| Data Completeness | | +| Data Selection | | +| Data Treatment | | +| Sampling Procedure | | +| Data Collection Period | | -## Unit Process Use Information -Use Advice -: +## References +{sources_md} -Preferred Provider UPs -: -Suggested Downstream UP -: +# Use Information + +Use Advice +: _Copy text from "Intended Application" field in openLCA._ + +Access and Restrictions +: _Copy text from "Access and use restrictions" field in openLCA._ -## References -{sources_md} # Document Control Information -Date Created +Report Created: : {create_date} -Point of Contact +Point of Contact: : {project_poc_md} -Revision History +Revision History: : {version_number} -How to Cite This Document -: National Energy Technology Laboratory (NETL), Combustion of Natural Gas, 11/01/2014, https://edx.netl.doe.gov/dataset/lca-up-combustion-of-natural-gas, OSTI ID: 1509357 +How to Cite This Document: +: _Copied from EDX submission._ Archived : False @@ -534,7 +553,7 @@ def empty_template(self): - Process Type: _State whether this is a Unit Process or System Process_ - Inventory Method: _State whether this is Attributional or Consequential_ - Technology Type: _High level technology categorization, such as natural gas, or bio fuels_ -- Unit Process Boundary: Gate-to-Gate (GG) +- Unit Process Boundary: _State whether the process is Cradle-to-Grave, Cradle-to-Gate, Gate-to-Gate, etc._ - Co-products: _List names, if applicable; otherwise, N/A_ - Co-product allocation: _If applicable; otherwise, N/A. Examples include system expansion and mass allocation; list what is used and if there are any restrictions._ - Scenarios: _Yes or no (e.g., link to [Scenarios](#scenarios) section)_ From a1b3f18a425482103f303c92d562ce9bc8c376f5 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 12 Mar 2026 11:55:39 -0400 Subject: [PATCH 15/19] update setup and create TOML --- pyproject.toml | 41 +++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f134471 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "up_template" +version = "3.0.0" +description = "The NETL Unit Process Template and Report Generator" +readme = "README.md" +authors = [ + { name = "Tyler W. Davis" }, + { name = "Priyadarshini" }, + { name = "Joseph Chou" }, + { name = "Matt Jamieson", email = "matthew.jamieson@netl.doe.gov" } +] +license = "CC0-1.0" +requires-python = ">=3.9" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "netlolca @ git+https://github.com/NETL-RIC/netlolca", + "openpyxl", + "pandas", + "sympy", + "tabulate", + "jupyterlab", +] + +[project.urls] +Homepage = "https://github.com/NETL-RIC/up_template" + +[tool.setuptools] +packages = ["up_template"] diff --git a/setup.py b/setup.py index 50e04e8..d97318b 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ long_description_content_type="text/markdown", url="https://github.com/NETL-RIC/up_template", classifiers=[ - 'Development Status :: 5 - Production/Stable', + 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication', 'Programming Language :: Python :: 3', From 23412ec321938f3787008a1e9f469df23e86579a Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 12 Mar 2026 11:56:22 -0400 Subject: [PATCH 16/19] general updates to README --- README.md | 77 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ed58462..67396a7 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,56 @@ The foundation of all tools in this package is the [NetlOlca](https://doi.org/10 ## Jupyter Notebook Unit Process Template -One of the main use cases for this tool is a new template based on Jupyter Notebook for reporting LCA unit processes. +The unit process template is a markdown file, organized by headers, to capture the essential information that goes into the creation of a life cycle unit process. +Capturing key metadata is critical for reproducibility of unit processes and for transparency to enable its application. -The new template is provided at the top-level of this repository (up_template.ipynb). +Two Python classes were developed to support the generation of this report: -![](img/package_uml.png) +1. NetlOlcaReport (up_template/NetlOlcaReport.py) +2. Interface (up_template/Interface.py) -Two additional classes were developed to support this interactive report generator: +The NetlOlcaReport class provides the methods for converting openLCA entity data into usable data frames and summaries and writing them into markdown format that can be converted into other reportable formats (e.g., HTML, Microsoft Word, and PDF). +The file format conversion depends on a user's local installation of [pandoc](https://pandoc.org/), a free and open-source tool for converting between different markup formats. -1. NetlOlcaReport (netlolca/NetlOlcaReport.py) -2. Interface (netlolca/Interface.py) +To create a blank template in markdown: -The NetlOlcaReport class provides intermediate methods for converting openLCA entity data into usable data frames and generating a summary of the results into a markdown format that can be converted into other reportable formats (e.g., HTML, Microsoft Word, and PDF). -The file format conversion depends on a user's local installation of [pandoc](https://pandoc.org/), a free and open-source tool for converting between different markup formats. +```python +>>> from netlolca import NetlOlca +>>> from up_template import NetlOlcaReport +>>> r = NetlOlcaReport(NetlOlca()) +>>> r.save_markdown(blank=True) +``` + +You may edit this markdown file before rendering to HTML. +If you rename the file, be sure to update the reference name in the Python class. + +```python +>>> r.reference_name = "new_report" # don't include the file extension +``` + +To generate the HTML version of the report, run the conversion method (PDF and Microsoft Word formats are also available). + +```python +>>> r.convert_to_html() # requires pandoc install +>>> r.convert_to_pdf() # requires additional LaTeX install +>>> r.convert_to_word() +``` + +Automated methods are available in the NetlOlcaReport class, which attempt to read and extract metadata from an openLCA database and map it to the report template. +Similar to above, the steps to connect the NetlOlcaReport class to an openLCA database are as follows: + +```python +>>> n = NetlOlca() +>>> n.connect() # establish connection via IPC service (default port 8080) +>>> n.read() # read database UUIDs +>>> r = NetlOlcaReport(n) +>>> ps_uuid = r.product_systems[0] # Get UUID of lone Product System +>>> r.fetch_data(ps_uuid) # Scrape database for metadata +>>> r.save_to_html() # create MD and HTML report files +``` -The recommended app for running the UP Template is [JupyterLab](https://jupyter.org/), the latest web-based interactive development environment for computational notebooks. +The Interface.py is an experimental class that guides users through metadata review and gap-filling. +This is best accommodated through [JupyterLab](https://jupyter.org/), the latest web-based interactive development environment for computational notebooks. JupyterLab may be installed using Python's `pip` or conda's `install` commands. To start Jupyter Lab, run the following command (after installing) in the parent folder where your up_template.ipynb is located: @@ -37,11 +72,12 @@ This should start the Jupyter notebook server and automatically launch the landi up-template/ ├── calculations/ <- store for auxiliary Excel workbooks to document │ │ calculations in the UP template - │ └── calculation_template.xlsx <-- template for calculations + │ └── calculation_template.xlsx <- template for calculations │ - ├── data/ <- empty folder for data files (e.g., JSON-LD) + ├── (data/) <- folder for data files (e.g., JSON-LD) │ ├── dockers/ (for getting things to run in Docker) + │ ├── USERGUIDE.md <- Docker instructions │ ├── gdt_server/ │ │ ├── README.md │ │ ├── build.bat @@ -56,6 +92,7 @@ This should start the Jupyter notebook server and automatically launch the landi │ ├── build.sh │ ├── get-docker.bat │ ├── get-docker.sh + │ ├── requirements.txt │ ├── run.bat │ └── run.sh │ @@ -65,13 +102,7 @@ This should start the Jupyter notebook server and automatically launch the landi │ └── Makefile <- source files to documentation (e.g., html) │ ├── img/ <- image resources for notebooks and README - │ ├── banner.png (293 KB) - │ ├── boundary_diagram.png (205 KB) - │ ├── logo_doe-netl_white_1000x176.png (30 KB) - │ ├── netl_logo_100x52.png (14 KB) - │ ├── netl_logo_1153x599.png (26 KB) - │ ├── netl_logo2.svg (2 KB) - │ ├── netl_logo2_320x251.png (13 KB) + │ ├── ipynb-token.png (92 KB) │ └── package_uml.png (281 KB) │ ├── (output/) <- Created when running UP Template to store the @@ -79,11 +110,13 @@ This should start the Jupyter notebook server and automatically launch the landi │ ├── resources/ │ ├── after_body.html + │ ├── banner.png <- header in up_template.ipynb │ ├── before_body.html - │ ├── banner.png - │ ├── netl_logo_100x52.png - │ ├── logo_doe-netl_white_1000x176.png - │ ├── (other logo files?) + │ ├── boundary_diagram.png + │ ├── logo_doe-netl_white_1000x176.png <- footer in template.docx + │ ├── netl_logo_100x52.png <- header in template.docx + │ ├── netl_logo_1153x599.png <- header in before_body.html + │ ├── styles.css │ └── template.docx │ ├── up_template/ <- Source code for this package. From 8c6a6b0b5ac2e2086a545eb44ffb89e7b780b510 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 12 Mar 2026 11:59:02 -0400 Subject: [PATCH 17/19] minor QoL improvements Create a product systems class property list (easier to grab UUID). Add HTML page title to convert. Provide a method to write blank MD template in save_markdown. --- up_template/NetlOlcaReport.py | 54 ++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index f178b7c..c11fa62 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -20,14 +20,12 @@ >>> n.connect() # establish with IPC Server >>> n.read() # read openLCA database >>> r = NetlOlcaReport(n) # creates 'output' folder ->>> ps_id = r.netlolca.get_spec_ids( # find 1st product system UUID -... r.netlolca.get_spec_class("Product system") -... )[0] +>>> ps_id = r.product_systems[0] # find 1st product system UUID >>> r.fetch_data(ps_id) # automated data scraping >>> r.convert_to_html() # create .md; render to .html Last Edited: - 2026-03-05 + 2026-03-12 """ __all__ = [ "NetlOlcaReport", @@ -66,7 +64,7 @@ ############################################################################## # CLASSES ############################################################################## -class NetlOlcaReport: +class NetlOlcaReport(object): """A report generating class for the Jupyter Notebook unit process template. @@ -188,6 +186,16 @@ def __init__(self, netlolca_instance): "Government or any agency thereof." ) + # \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + # Class Properties + # //////////////////////////////////////////////////////////////////////// + @property + def product_systems(self): + """List of product system UUIDs""" + return self.netlolca.get_spec_ids( + self.netlolca.get_spec_class("Product system") + ) + # \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ # Class Function Definitions # //////////////////////////////////////////////////////////////////////// @@ -239,6 +247,8 @@ def convert(self, from_file, to_file): 'https://cdnjs.cloudflare.com/ajax/libs/concrete.css/3.0.0/concrete.min.css', '--css', 'resources/styles.css', + '--metadata', + f'title=NETL UP Library | {self.reference_name}', '--mathjax', '--include-before-body', 'resources/before_body.html', @@ -1187,18 +1197,18 @@ def get_file_name(self, ext=".md"): if self.reference_name: # No spaces in file names p1 = re.compile("\\s+") - r_name = re.sub(p1, "_", self.reference_name) + r_name = re.sub(p1, "-", self.reference_name) # No special characters in file names p2 = re.compile("[/@.,&'\\\\\\(|\\)<>#;]+") - r_name = re.sub(p2, "_", r_name) + r_name = re.sub(p2, "-", r_name) - # Reduce extra underscores - p3 = re.compile("_+") - r_name = re.sub(p3, "_", r_name) + # Reduce extra dashes + p3 = re.compile("-+") + r_name = re.sub(p3, "-", r_name) - # Drop trailing underscore - if r_name[-1] == "_": + # Drop trailing dash + if r_name[-1] == "-": r_name = r_name[0:-1] r_name += ext @@ -1325,17 +1335,33 @@ def read_report_markdown(self, md_file=None): else: self.md = md_txt - def save_markdown(self): + def save_markdown(self, blank=False): """Generate markdown report and save to file. + Parameters + ---------- + blank : bool (optional) + Whether to save a blank template. + Defaults to false. + Notes ----- 1. The file is saved to the globally defined output directory with a file name that defaults to the reference process name. 2. This method re-creates the markdown report based on fetched data; any changes to the original markdown will be overwritten! + + Examples + -------- + >>> r = NetlOlcaReport(NetlOlca()) # initialize empty class + >>> r.save_markdown(blank=True) # create empty template """ - self.md = self.create_report_markdown() + if blank: + if self.reference_name is None: + self.reference_name = "blank-report" + self.md = self.empty_template() + else: + self.md = self.create_report_markdown() file_path = self.get_file_path('md') with open(file_path, 'w', encoding='utf-8', newline='\n') as f: f.write(self.md) From 9343c546dff96fd2f9a63c96bc756d8404538d03 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Thu, 12 Mar 2026 12:28:41 -0400 Subject: [PATCH 18/19] uptick to v4.5 Consistent versioning with respect to the pprevious versions. --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f134471..9844abe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "up_template" -version = "3.0.0" +version = "4.5.0" description = "The NETL Unit Process Template and Report Generator" readme = "README.md" authors = [ diff --git a/setup.py b/setup.py index d97318b..6a7711f 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name="up_template", - version="3.0.0", + version="4.5.0", license="CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", packages=['up_template'], install_requires=[ From eaf588a3709fa4f5da50b4f09d1f46e836330b62 Mon Sep 17 00:00:00 2001 From: dt-woods Date: Fri, 13 Mar 2026 11:05:56 -0400 Subject: [PATCH 19/19] correct page titling for HTML --- up_template/NetlOlcaReport.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/up_template/NetlOlcaReport.py b/up_template/NetlOlcaReport.py index c11fa62..8cd17ad 100644 --- a/up_template/NetlOlcaReport.py +++ b/up_template/NetlOlcaReport.py @@ -25,7 +25,7 @@ >>> r.convert_to_html() # create .md; render to .html Last Edited: - 2026-03-12 + 2026-03-13 """ __all__ = [ "NetlOlcaReport", @@ -248,7 +248,8 @@ def convert(self, from_file, to_file): '--css', 'resources/styles.css', '--metadata', - f'title=NETL UP Library | {self.reference_name}', + f'pagetitle={self.reference_name}', + '--title-prefix=NETL UP Library', '--mathjax', '--include-before-body', 'resources/before_body.html',