+
\ No newline at end of file
diff --git a/html/blog/2026-04-03-bovaer_strategy-subsidies.html b/html/blog/2026-04-03-bovaer_strategy-subsidies.html
new file mode 100644
index 0000000..3b2c181
--- /dev/null
+++ b/html/blog/2026-04-03-bovaer_strategy-subsidies.html
@@ -0,0 +1,55 @@
+
+
+
\ No newline at end of file
diff --git a/planzero/blog.py b/planzero/blog.py
index 9e49a6f..ca499fc 100644
--- a/planzero/blog.py
+++ b/planzero/blog.py
@@ -205,6 +205,16 @@ def __init__(self):
draft=True,
)
+ @staticmethod
+ def generate_assets():
+ scaling = sim.simulation_result('Scaling')
+ scaling.by_ipcc_sector.save_as(
+ 'html/blog/2026-04-03-bovaer_by-ipcc-sector.html')
+ scaling.strategy_impact_echart('Scale_Bovaer').save_as(
+ 'html/blog/2026-04-03-bovaer_strategy-emissions.html')
+ scaling.strategy_subsidies_echart('Scale_Bovaer').save_as(
+ 'html/blog/2026-04-03-bovaer_strategy-subsidies.html')
+
class IPCC_HeavyDutyDieselVehicles(BlogPost):
"""Eighth in the sector-by-sector National Greenhouse Gas Inventory series:
diff --git a/planzero/html.py b/planzero/html.py
index 669f719..9f91ee6 100644
--- a/planzero/html.py
+++ b/planzero/html.py
@@ -121,6 +121,11 @@ class StackedAreaEChart(HTML_element):
other_series: list[EChartSeriesBase]
legend: dict | None = None
+ def save_as(self, filepath):
+ # called from Makefile to create snapshots for posts
+ with open(filepath, 'w') as ofile:
+ ofile.write(self.as_html())
+
def as_html(self):
newline = '\n'
return f"""
diff --git a/planzero/sim.py b/planzero/sim.py
index 237febd..3358897 100644
--- a/planzero/sim.py
+++ b/planzero/sim.py
@@ -207,11 +207,11 @@ def strategy_emissions_diffs(self, strategy_name:str, eps_kt:float) -> dict:
continue
if base_total is None:
- diff = abl_total
+ diff = -abl_total
elif abl_total is None:
- diff = -base_total
+ diff = base_total
else:
- diff = abl_total - base_total
+ diff = base_total - abl_total
assert diff.v_unit == u.kt_CO2e, diff.v_unit
if np.abs(diff.values[1:]).max() > eps_kt:
@@ -280,14 +280,14 @@ def strategy_impact_echart(self, strategy_name: str) -> StackedAreaEChart:
baseline_total = baseline_emres.total()
ablated_total = ablated_emres.total()
impact_data = EChartSeriesData(
- ablated_total - baseline_total,
+ baseline_total - ablated_total,
times=self.year_times,
v_unit=u.kt_CO2e,
url=None
)
other_series=[
EChartSeriesBase(
- name='Net Emissions Avoided',
+ name='Net Emissions Delta',
lineStyle=EChartLineStyle(color='#303030', width=2),
itemStyle=EChartItemStyle(color='#303030'),
data=impact_data,
@@ -300,7 +300,7 @@ def strategy_impact_echart(self, strategy_name: str) -> StackedAreaEChart:
text=f'Emissions Impact: {strategy_name.replace("_", " ")}',
subtext=f'Annual kt CO2e saved in {self.simulation_name}'),
xAxis=EChartXAxis(data=self.year_ints),
- yAxis=[EChartYAxis(name='Emissions Saved (kt CO2e)')],
+ yAxis=[EChartYAxis(name='Emissions Delta (kt CO2e)')],
stacked_series=[
EChartSeriesStackElem(
name=catpath_plus,
@@ -357,7 +357,7 @@ def strategy_subsidies_echart(self, strategy_name: str) -> StackedAreaEChart:
yAxis=EChartYAxis(name='Subsidies Required (CAD, Millions)'),
stacked_series=[
EChartSeriesStackElem(
- name=f'{program}, {reason}',
+ name=f'{program.value}: {reason}',
data=EChartSeriesData(
diff,
times=self.year_times,
diff --git a/planzero/strategies/strategy2.py b/planzero/strategies/strategy2.py
index ce47685..bd27740 100644
--- a/planzero/strategies/strategy2.py
+++ b/planzero/strategies/strategy2.py
@@ -33,7 +33,7 @@ class Scale_Bovaer(Strategy2):
cattle farmers who are modelled as being open to Bovaer usage
(according to the assumptions in
Bovaer Adoption Limit)
- actually go for it. This adoption is modelled as a nation-wide
+ are subsized by public funds, and go for it. This adoption is modelled as a nation-wide
proportionality, not province-by-province.
"""
# TODO: add a see-also type mechanism, to look at the effects
@@ -41,7 +41,7 @@ class Scale_Bovaer(Strategy2):
@computed_field
def short_description(self) -> str:
- return f"Model that farmers who are open to using Bovaer actually start administering it."
+ return f"Model that farmers who are open to using Bovaer are subsidized to start administering it."
def see_also_html(self, context_vars) -> list[str]:
sources = [
From 568178d4dc4b9ddb889239f442ca7b42d421b0e5 Mon Sep 17 00:00:00 2001
From: James Bergstra <171276+jaberg@users.noreply.github.com>
Date: Tue, 12 May 2026 16:55:20 -0400
Subject: [PATCH 35/38] working over glossary.py, halfway through
---
app.py | 2 +
html/blog/2026-04-12-about.html | 32 ++-
html/glossary.html | 11 +-
planzero/blog.py | 7 +-
planzero/glossary.py | 340 +++++++++++++++++++++++++-------
test_200.py | 1 +
6 files changed, 303 insertions(+), 90 deletions(-)
diff --git a/app.py b/app.py
index ba27ee1..e407f11 100644
--- a/app.py
+++ b/app.py
@@ -324,6 +324,7 @@ def get_blog_html(post_name: str):
@app.get("/blog/{post_name}", response_class=HTMLResponse)
+@app.get("/post/{post_name}", response_class=HTMLResponse)
async def get_blog(request: Request, post_name:str):
try:
html = get_blog_html(post_name)
@@ -355,6 +356,7 @@ async def get_glossary(request: Request):
)
@app.get("/index.html", response_class=HTMLResponse)
+@app.get("/posts/", response_class=HTMLResponse)
@app.get("/", response_class=HTMLResponse)
async def get_index(request: Request, unpublished:bool=HOME_SHOW_UNPUBLISHED_POSTS):
return templates.TemplateResponse(
diff --git a/html/blog/2026-04-12-about.html b/html/blog/2026-04-12-about.html
index 2f79ab3..d046544 100644
--- a/html/blog/2026-04-12-about.html
+++ b/html/blog/2026-04-12-about.html
@@ -36,7 +36,7 @@
Introduction
My intent is partly to prepare
for a future in which others may contribute to PlanZero,
and partly to keep myself focused by articulating a process that I believe I should follow.
-This post re-articulates the project mission and vision, and recognizes the
+This post refreshes the project mission and vision, and recognizes the
contributors that are helping to bring it together.
This post supercedes the thinking in
"Contributing (even for myself)", from January 2026, and replaces the
previous content of the About page,
@@ -60,7 +60,8 @@
About this project
-There have been, and thankfully continue to be, many efforts to model future emissions in the world:
+There have been, and thankfully continue to be,
+many efforts to model future emissions in the world:
the effect they have on the climate, and the effect various technologies may exert on emission rates.
As I build up a bibliography (not yet started as of April 2026) this PlanZero site will situate itself in the context of ongoing and previous work.
I believe that PlanZero is differentiated from other efforts in (a) focusing on Canada, and
@@ -141,29 +142,40 @@
Guidance Re: Posts
Preface: Revision Control as Mechanism and Inspiration
As a preface to explaining how posts are supposed to work in PlanZero,
-it is worth saying how revision control, and git in particular, have at least revolutionized, if not fundamentally enabled,
+it is worth saying how revision control,
+and git in particular,
+have at least revolutionized, if not fundamentally enabled,
open-source software.
Open-source software is developed in many ways, but the most canonical style is
by a loosely-organized community
-of contributors, each of whom has a copy of the software (called a branch, or fork of the code), and can develop it
-however, and whenever they please (by making commits of changes to their branch).
+of contributors, each of whom has a copy of the software
+(called a {{siteref("Git Branch", "branch")|safe}},
+or {{siteref("Git Fork", "fork")|safe}} of the code), and can develop it
+however, and whenever they please (by
+making {{siteref("Git Commit", "local commits")|safe}} of changes to their
+{{siteref("Git Branch", "branch")|safe}} of source code).
Development would not be efficient,
if not for revision-control systems that
allow such developers to stitch their private improvements back together into
-a coherent, improved, software system (this stitching is called a merge in git).
+a coherent, improved, software system (this stitching is called a
+{{siteref("Git Merge", "merge")|safe}} in git).
Despite challenges such as weak coordination and predictable governance issues,
open-source communities have developed and
maintained
some of the most complex and most stable software in the world,
-including git itself (of course),
+including git itself,
the Linux operating system,
simulation tools for mathematical modelling,
and many
models used for atmospheric, ocean, and climate science.
+I have made PlanZero an open source project because I believe that
+that it will require the support, at some point, of an open-source community.
-This pattern applies at two conceptual levels with regards to PlanZero.
+This pattern of iteratively
+patching up and extending open source software
+applies at two conceptual levels with regards to PlanZero.
As a lower level, PlanZero follows this pattern, by using git via GitHub.
As a higher level, PlanZero uses posts to mark significant changes
to the underlying project code, including the structure and visualizations
@@ -244,7 +256,7 @@
Draft Status
post introducing the superceding content
All such changes will be tracked in git in any event.
-Git history should only be amended in order to remove accidentally committed
+{{siteref("Git History", "Git history")|safe}} should only be amended in order to remove accidentally committed
material, including sensitive or confidential material.
@@ -269,7 +281,7 @@
Post Dates
the amendment should mention a date e.g. "(Edited April 25, 2026: new post X covers this topic differently...)" but this date too can be approximate.
-PlanZero's git history has a precise history
+PlanZero's {{siteref("Git History", "git history")|safe}} records a precise history
of which files were edited in what ways on which dates and times.
This history is
detailed and accurate, although it reflects the
diff --git a/html/glossary.html b/html/glossary.html
index 21fa16d..5aa7b29 100644
--- a/html/glossary.html
+++ b/html/glossary.html
@@ -28,11 +28,11 @@
PlanZero Glossary
Term list:
-
+
{% for clsname, obj in sorted(planzero.glossary.glossary_terms.items()) %}
@@ -52,11 +52,14 @@
{% endif %}
-{% if obj.see_also %}
+{% if obj.see_also or obj.as_discussed_in_posts %}
See Also
{% for term, ref_text in obj.see_also.items() %}
- - {{term}} - {{ref_text}}
+ - {{term.replace("_", " ")}} - {{ref_text}}
+ {% endfor %}
+ {% for obj, hashref, descr in obj.as_discussed_in_posts %}
+ - [Post] {{obj.title}} - {{descr}}
{% endfor %}
diff --git a/planzero/blog.py b/planzero/blog.py
index ca499fc..80016a9 100644
--- a/planzero/blog.py
+++ b/planzero/blog.py
@@ -30,6 +30,10 @@ class BlogPost(BaseModel):
concept_only: bool = False # there is no html for this post object
tags: set[str] = set()
+ @property
+ def siteref(self):
+ return f'/post/{self.url_filename}'
+
def __init__(self, **kwargs):
if 'about' not in kwargs:
kwargs = dict(kwargs, about=self.__class__.__doc__)
@@ -179,7 +183,8 @@ def __init__(self):
title='About this project: rewriting and expanding planzero.ca/about',
url_filename="2026-04-12-about",
author="James Bergstra",
- tags={BlogTag.About,},
+ tags={BlogTag.About,
+ },
draft=True,
)
diff --git a/planzero/glossary.py b/planzero/glossary.py
index 741643a..7deb0df 100644
--- a/planzero/glossary.py
+++ b/planzero/glossary.py
@@ -20,6 +20,7 @@ def siteref(term, text=None):
from .blog import latex
+from . import blog
from . import barriers
from . import cattle
from . import strategies
@@ -64,8 +65,9 @@ def all_names(self) -> list[str]:
return rval + self.aka
@computed_field
- def as_discussed_in_posts(self) -> dict[str, str]:
- return {}
+ def as_discussed_in_posts(self) -> list[tuple[object, str, str]]:
+ # post, hashtarget, intro txt
+ return []
@computed_field
def code_links(self) -> dict[str, str]:
@@ -97,6 +99,12 @@ def lref(term, text=None):
return glossary_terms_w_aka[term].local_ref(text)
return dict(
CO2e=latex(r'\mathrm{CO}_2\mathrm e '),
+ CO2=latex(r'\mathrm{CO}_2'),
+ CH4=latex(r'\mathrm{CH}_4'),
+ N2O=latex(r"\mathrm N_2 \mathrm O"),
+ SF6=latex(r"\mathrm{SF}_6"),
+ NF3=latex(r"\mathrm{NF}_3"),
+ degrees=latex(r'^\circ'),
lref=lref,
)
@@ -175,14 +183,28 @@ def code_refs(self) -> dict[str, object]:
class Dynamic_Element(GlossaryTerm):
"""A PlanZero modelling data structure for representing a modelling
- assumption, that one or more
- {{lref("Time Series", "time series")|safe}} follows a formula.
+ assumption, and defining one or more
+ {{lref("Time Series", "time series")|safe}}.
A dynamic element is expected to be a Python code object, that is
a subclass of either a
{{lref("Strategy")|safe}} or a
{{lref("Barrier")|safe}}.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'Time Series': 'timeseries are the inputs and outputs of dynamic elements',
+ 'Model': 'models are sets of dynamic elements',
+ 'Simulation': 'dynamic elements provide the initialization and recurrence logic to define time series by simulation',
+ }
+
+ @computed_field
+ def as_discussed_in_posts(self) -> list[tuple[object, str, str]]:
+ return [
+ (blog.Glossary(), '#dynelem', "see section on Computation and Simulation"),
+ ]
+
@property
def code_refs(self) -> dict[str, object]:
return {
@@ -222,7 +244,7 @@ class Barrier(GlossaryTerm):
that is not optional, that is, one whose omission would sacrifice the
validity of a model.
- PlanZero terminology may feel a bit cynical in this regard, but yes, in
+ PlanZero terminology may feel a bit cynical in this regard, but in
this terminology, all of the following would qualify as barriers:
- regulations
@@ -234,8 +256,8 @@ class Barrier(GlossaryTerm):
- the laws of physics
-
I borrow the term from {{lref("EGFS")|safe}} but risk mis-appropriating it
- as the use in a computational modelling framework is, admittedly, a stretch.
+
I borrow the term from {{lref("EGFS")|safe}} but its
+ use in a computational modelling framework is, admittedly, a stretch.
"""
@@ -275,24 +297,25 @@ class IPCC_Sector_Contributor(GlossaryTerm):
class Emission_Factor(GlossaryTerm):
"""
- IPCC sector emissions are generally defined as a sum of products (e.g.
- amount of activity
- multiplied by emissions per unit of activity,
- summed over one or more activities that count toward the category).
- Each of the emission-contributing activities corresponds
- to an {{lref("Driver")|safe}} and
- the emission of each greenhouse gas per unit of activity is referred to as
- an Emission Factor.
-
-
- An Emission Factor is a time series, whose unit is an amount of
- mass (of greenhouse gas) per unit activity (or if not "activity",
- whatever makes sense for the {{lref("Driver", "driver")|safe}}.
"""
+ An emission factor is a constant of proportionality between
+ an emission driver and the amount of some emitted greenhouse gas.
+ """
+
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'KPI': "an emission factor is one of PlanZero's base KPI types",
+ 'NIR_Model': "a model of Canada's future emissions",
+ 'Driver': "a quantity of activity or physical stock that causes emissions in proportion to one or more emission factors",
+ 'Greenhouse Gas': "an emission factor is a constant of proportionality to one greenhouse gas",
+ 'Emissions': "the result of multiplying an emission factor by a driver",
+ }
class Driver(GlossaryTerm):
- """
A quantity of activity or stock, typically associated with a province or
- territory.
+ """
A driver, in the context of a model,
+ is a quantity of activity or of physical stock,
+ that is typically associated with a province or territory.
{{lref("Barrier")|safe}} dynamic elements can register
{{lref("Time Series", "time series")|safe}} as drivers.
Drivers are meant to drive emission KPIs via emission factors,
@@ -315,6 +338,15 @@ class Driver(GlossaryTerm):
per year in a non-interpolating time series.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'NIR_Model': 'a model of national emissions',
+ 'Emission Factor': 'the constant of proportionality of a driver to emissions',
+ 'Subsidy Factor': 'the constant of proportionality of a driver to subsidy',
+ }
+
+
class Subsidy_Factor(GlossaryTerm):
"""
A subsidy factor is a constant of proportionality between
@@ -323,37 +355,31 @@ class Subsidy_Factor(GlossaryTerm):
class Critical_Success_Factor(GlossaryTerm):
- """
A Critical Success Factor is a dynamic element tied to an
- {{lref("IPCC Sector")|safe}},
- representing a mathematical decomposition of what
- would be required to reduce or maintain emissions in that category.
- Category emissions are typically a sum of products (e.g. amount of activity
- multiplied by emissions per unit of activity for one or more activities),
- and the summed terms are typically the Critical Success Factors (e.g.
- each emission-contributing activity is one Critical Success Factor).
-
- TODO EXAMPLE
-
- Critical Success Factors are used in the visualization and communication
- of the effects of strategies and barriers.
- Althouth the scope (or granularity) of critical success factors is not
- generally obvious,
- I hope that PlanZero can make itself useful with a relatively stable set.
- They are defined to be one-per-emissions-contributing-activity so that
- PlanZero can visualize the emissions contributing to a sector as a stacked
- line chart of Critical Success Factors.
-
-
- The term Critical Success Factor has a long history.
- I believe I'm fairly appropriating it, I learned about it from
- {{lref("EGFS")|safe}}.
+ """
A Critical Success Factor
+ is a necessary condition of a KPI to achieve an objective.
+ For example, the KPI must be within a certain range of values
+ for any or all of some period of time.
+ The term Critical Success Factor has a
+ long history.
+ PlanZero's use of the term is based on the definition from {{lref("EGFS")|safe}}.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'EGFS': 'The Executive Guide to Facilitating Strategy',
+ 'Models Section': 'models section of PlanZero website',
+ 'Critical Success Factor': 'an emissions contribution to an IPCC Sector',
+ }
+
@computed_field
def as_discussed_in_posts(self) -> dict[str, str]:
- return {'A Glossary of terms...':
- '/blog/2026-04-19-glossary#critical_success_factor'}
+ return [
+ (blog.Glossary(),
+ '#formalizing_csfs',
+ 'see section "Emissions and Costs as Critical Success Factors"'),
+ ]
@computed_field
def aka(self) -> list[str]:
@@ -362,17 +388,25 @@ def aka(self) -> list[str]:
class Key_Performance_Indicator(GlossaryTerm):
"""
- A Key Performance Indicator (KPI) in PlanZero is the time series associated with
- a critical success factor.
- A critical success factor represents what must be true of one or more KPIs
- as a necessary (if not sufficient) condition to achieve a goal.
- Typically the KPI value must be within a certain range over some period of time.
+ A Key Performance Indicator (KPI) in PlanZero is a time series that
+ is registered to participate in the calculation of emissions or subsidies.
+ A
base KPI is a driver, an emission factor, or a subsidy factor.
+ A
derived KPI is the product of a driver with an emission factor,
+ or the product of a driver with a subsidy factor, or the result of summing
+ together other derived KPIs toward e.g. national totals.
"""
@computed_field
def aka(self) -> list[str]:
return ['KPI']
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'CSF': "target values for target times of a KPI time series, in order to achieve an objective",
+ 'NIR_Model': "a model of Canada's future emissions",
+ }
+
class NIR_Model(GlossaryTerm):
@@ -417,15 +451,33 @@ class Stochastic_Model(GlossaryTerm):
follow some distribution over possible outcomes, as defined by the model.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'NIR_Model': """Model of Canada's national emissions in the style
+ of the National Inventory Reports submitted to UNFCCC""",
+ 'Deterministic_Model': "A model that corresponds to a unique scenario",
+ 'Model': "A set of dynamic elements that can be simulated",
+ }
+
class Deterministic_Model(GlossaryTerm):
"""A deterministic model is a model that corresponds to a specific
scenario, and has no randomness.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'NIR_Model': """Model of Canada's national emissions in the style
+ of the National Inventory Reports submitted to UNFCCC""",
+ 'Model': "A set of dynamic elements that can be simulated",
+ 'Stochastic_Model': "A model that corresponds to a distribution over possible scenarios",
+ }
+
-class Models_Section(GlossaryTerm):
- """The models section of the planzero.ca website:
- https://planzero.ca/models/"""
+class Simulations_Section(GlossaryTerm):
+ """The Simulations section of the planzero.ca website:
+
https://planzero.ca/simulations/"""
class About_Section(GlossaryTerm):
@@ -471,6 +523,9 @@ class Git(GlossaryTerm):
def see_also(self) -> dict[str, str]:
return {
'GitHub': "git hosting for PlanZero",
+ 'Git Branch': "maintain versions of code with git branches",
+ 'Git Commit': "record source file changes to a git branch",
+ 'Git Merge': "merge changes from one branch into another",
}
class Git_Merge(GlossaryTerm):
@@ -494,15 +549,27 @@ def see_also(self) -> dict[str, str]:
}
class Git_Branch(GlossaryTerm):
- """A sequence of git commits (sometimes a graph)
+ """A git branch is a way of tracking a single version of a set of files.
+ Technically, it is a sequence of git commits (sometimes a graph)
to source files leading from the initially empty project
to some version that's full of files.
- The site is populated by deploying the "main" branch.
+ The PlanZero site is populated by deploying the "main" branch.
Anyone is welcome to suggest changes to main by creating a pull request on github,
requesting that the main branch merge changes from another branch that
they've created.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'Main_Branch': "the branch from which the PlanZero site is generated",
+ 'GitHub_Pull_Request': "a request to merge branches made via GitHub",
+ 'Git_Commit': "change a branch by adding a commit, which incorporates changes to local files",
+ 'Git_Merge': "changes from one branch can be merged into another",
+ 'Git_Graph': "the commits and merges to a branch define a directed acyclic graph with a single source node (the beginning of development) and a single sink node (the current state of the branch)",
+ }
+
+
class Main_Branch(GlossaryTerm):
"""PlanZero on GitHub generally has multiple branches.
The "main branch" is special, in that it is the one used to deploy the
@@ -528,15 +595,33 @@ def see_also(self) -> dict[str, str]:
'Main_Branch': "the code from which the site is generated",
}
-class Repository(GlossaryTerm):
- """Code repository, on GitHub"""
+class GitHub_Repository(GlossaryTerm):
+ """A GitHub code repository, or repo, is a GitHub-defined entity,
+ it is a major point of configuration,
+ especially for billing and permissions.
+ A repo contains all of the code and files
+ for one or more git branches.
+ A repo may be created from some standard initial state (such as being empty),
+ or it may be created by forking another repo.
+ My ("James Bergstra", aka "jaberg") PlanZero repo is
here.
+ If a repo is not created by forking, then it may be called a "Source Repo"
+ or "Upstream Repo".
+ """
@computed_field
def aka(self) -> list[str]:
- return ['repo']
+ return ['repo', 'repository']
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'GitHub': "the site that hosts GitHub code repositories",
+ 'Git': "the version control system upon which GitHub operates",
+ 'Git_Fork': "A repo can be forked to create a downstream copy of an upstream repo"
+ }
-class Git_Fork(GlossaryTerm):
+class GitHub_Fork(GlossaryTerm):
"""Public GitHub {{lref("repo", "repos")|safe}} can be "forked" by users
who wish to make and publish their own modifications. If you have a GitHub
account, you can
fork
@@ -551,19 +636,42 @@ def see_also(self) -> dict[str, str]:
return {
'Git_Branch': "a codebase version within a fork",
'GitHub_Pull_Request': "a request to merge branches, possibly across forks",
+ 'GitHub_Repository': "A GitHub repo may be a fork of another repo",
}
+
class Git_Commit(GlossaryTerm):
"""A "commit" is an increment of change to a codebase, across one or more
- changed files.
+ changed files. It is a node in the graph of changes that make up a git
+ repository.
"""
@property
def see_also(self) -> dict[str, str]:
return {
- 'Git_Branch': "a sequence of commits",
+ 'Git_Branch': "a named graph of commits corresponding to a single version of set of files",
}
+class Git_Graph(GlossaryTerm):
+ """A set of commits and merges that build on one another form a graph
+ representing all of the development on a project.
+ Try visualizing the graph for any git project by using purpose-built visual tools,
+or running a command such as git log --all --decorate --oneline --graph
+ """
+
+ @computed_field
+ def aka(self) -> list[str]:
+ return ['Git History']
+
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'Git_Commit': "a node of a git [change] graph",
+ 'Git_Repo': "a copy of a git graph",
+ 'Git_Branch': "a named subgraph of ancestors of a particular commit",
+ }
+
+
class GitHub(GlossaryTerm):
"""GitHub (site, wikipedia) is a web service for using the git version
control system over the internet to collaborate on software projects. Circa 2023, it was the world's largest source code host, with over 100 million developers, and 420 million code repositories.
@@ -572,12 +680,20 @@ class GitHub(GlossaryTerm):
@property
def see_also(self) -> dict[str, str]:
return {
+ 'Git': "version control software upon which GitHub is based",
+ 'GitHub_Repository': "git branches corresponding to one or more versions of a project's code and files",
+ 'GitHub_Fork': "a copy of a GitHub repository into another user's GitHub account",
'GitHub_Issue': "a future-work item on the PlanZero project",
}
class GitHub_Issue(GlossaryTerm):
- """Link to GH issues page, explain how they're used in PlanZero
+ """GitHub issues, such as the PlanZero issues,
+ are notices / reminders of future work.
+ Issues are used differently by different projects, some projects don't use
+ GitHub's issues at all.
+ PlanZero uses GitHub issues to record a variety of ideas and To-Do items,
+ but in particular, those that have been mentioned in posts.
"""
@computed_field
@@ -588,15 +704,16 @@ def aka(self) -> list[str]:
def see_also(self) -> dict[str, str]:
return {
'GitHub': """Code hosting for PlanZero""",
+ 'Post': """A report on a piece of work on PlanZero, sometimes linking to GitHub issues corresponding to next steps beyond the scope of the post itself""",
}
class GitHub_Pull_Request(GlossaryTerm):
- """One of GitHub's main features is a web interface for users
- to suggest changes to open source project code. Pull requests
+ """One of GitHub's main features is a web interface for people
+ to suggest code changes to each other. Pull requests
enable asynchronous loosely-coupled development over the net by
giving contributors and developers a place to talk about the changes,
- and implementing the {{lref("Git Merge", "merging")|safe}} of changes.
+ and possibly even {{lref("Git Merge", "merge")|safe}} those changes.
See
Github documentation for full description of this capability.
"""
@@ -609,11 +726,12 @@ def see_also(self) -> dict[str, str]:
return {
'Git_Branch': "a pull request is a request to merge two branches",
'Main_Branch': "submit a pull request to this branch when a new development is ready",
+ 'Git_Fork': "typically a pull request represents a request to merge code from a branch in one fork (maintained by one person) into a branch on another fork (maintained by another person)"
}
class EGFS(GlossaryTerm):
- """
Executive Guide to Facilitating Strategy, a book by Michael Wilkinson.
+ """
Wilkinson, M., The Executive Guide to Facilitating Strategy: featuring the Drivers Model, Atlanta: Leadership Strategies Publishing, 2011. Amazon
I've appropriated (hopefully not misappropriated) terms
of implementation planning ({{lref("Critical Success Factor")|safe}},
@@ -626,6 +744,20 @@ class EGFS(GlossaryTerm):
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'Strategy': "an ablatable model element, used to represent a strategy in a model",
+ 'Barrier': "a non-ablatable model element, used to model the interactions of time series with KPIs",
+ 'CSF': "target values for target times of a KPI time series, in order to achieve an objective",
+ 'KPI': "a time series registered to participate in emissions or subsidy calculations",
+ }
+
+ @computed_field
+ def as_discussed_in_posts(self) -> list[tuple[object, str, str]]:
+ return [
+ (blog.Glossary(), '#modelling', "see section on Modelling National Emissions"),
+ ]
class NIR(GlossaryTerm):
"""National Inventory Report,
@@ -646,6 +778,10 @@ class National_Greenhouse_Gas_Inventory(GlossaryTerm):
{{lref("NIR", "National Inventory Reports")|safe}}.
"""
+ @computed_field
+ def aka(self) -> list[str]:
+ return ['NGGI']
+
class National_Energy_Use_Database(GlossaryTerm):
"""
The National Energy Usage Database, is
@@ -716,7 +852,9 @@ def aka(self) -> list[str]:
@property
def see_also(self) -> dict[str, str]:
return {
- 'Natural_Resources_Canada': 'peer federal ministry'
+ 'Natural_Resources_Canada': 'peer federal ministry',
+ "NIR": "National Inventory Reports are prepared by ECCC",
+ "NGGI": "National Greenhouse Gas Inventory is maintained by ECCC",
}
class Net_Zero(GlossaryTerm):
@@ -749,12 +887,15 @@ def aka(self) -> list[str]:
class IPCC_Sector(GlossaryTerm):
- """An economic area for which Canada reports emissions
- in at least one {{lref("NIR")|safe}}, in accordance
+ """IPCC Sector is a PlanZero term, for
+ an economic area for which Canada tracks emissions
+ in the {{lref("NGGI")|safe}}, in accordance
with IPCC emissions reporting guidelines.
In PlanZero posts,
the term almost always refers to a sector that is not
a subtotal of other sectors.
+ PlanZero uses a set of 71 IPCC Sectors, which match
+ the ones used in NIR-2025.
"""
@@ -774,6 +915,18 @@ class Emissions(GlossaryTerm):
{{lref("National Greenhouse Gas Inventory")|safe}}.
"""
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'Driver': 'a level of activity or physical stock, multiplied by an emission factor to calculate emissions',
+ 'Emission_Factor': 'a factor of proportionality between drivers and emissions',
+ 'KPI': 'Emissions are Derived KPIs',
+ 'NIR_Model': "a model of Canada's future emissions, calculated by summing up emissions across IPCC Sectors",
+ "IPCC Sector": "the lowest levels of granularity in National Greenhouse Gas Inventory",
+ "National Greenhouse Gas Inventory": "the data from which NIRs are generated"
+ }
+
+
class Petrinex(GlossaryTerm):
"""
Petrinex facilitates efficient, standardized, safe, and
@@ -804,7 +957,44 @@ class Simulation(GlossaryTerm):
class Ablative_Analysis(GlossaryTerm):
"""
- For models with strategies, simulation involves rolling out the model
- with all of the strategies enabled, and then with each
- individual strategy being omitted.
+ Ablative analysis in PlanZero is the study of models with and without
+ certain elements (namely strategies), in order to characterize the impact
+ of those elements on the behaviour of the whole model.
+ """
+
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'Strategy': 'ablative analysis is used to evaluate strategies',
+ 'Simulation': 'implements ablative analysis',
+ }
+
+
+class Post(GlossaryTerm):
+ """A post on this site (see e.g. https://planzero.ca/posts/).
+
+ The About page
+ includes some guidelines for post content.
+ """
+
+
+class Greenhouse_Gas(GlossaryTerm):
+ """One, or a mixture, of the seven gases (or families of gases) assessed by the IPCC as
+ a significant factor in trapping heat within the Earth planetary system.
+ They are {{CO2|safe}}, {{CH4|safe}}, {{N2O|safe}},
+ HFCs, PFCs, {{NF3|safe}}, and {{SF6|safe}}.
"""
+
+ @property
+ def see_also(self) -> dict[str, str]:
+ return {
+ 'IPCC': 'International Panel on Climate Change defines greenhouse gases and reporting standards for signatories to the Paris Agreement',
+ 'NGGI': 'National Greenhouse Gas Inventory tracks Canadian greenhouse gas emissions',
+ }
+
+ @computed_field
+ def as_discussed_in_posts(self) -> list[tuple[object, str, str]]:
+ return [
+ (blog.GHG_Emissions(), '#h2_ghg', "see section on Greenhouse gases"),
+ ]
+
diff --git a/test_200.py b/test_200.py
index d17c501..200c5e6 100644
--- a/test_200.py
+++ b/test_200.py
@@ -25,6 +25,7 @@ def test_internal_links(endpoint):
# Extract all href attributes, excluding fragments and query parameters
html = response.text
links = re.findall(r'href=["\']([^"\'#?]+)["\']', html)
+ assert 'StrictUndefined' not in html
for link in set(links):
# Skip external protocols
From d468e81cf3acb47a5fce74bdd3555c98e3c99315 Mon Sep 17 00:00:00 2001
From: James Bergstra <171276+jaberg@users.noreply.github.com>
Date: Wed, 13 May 2026 15:25:49 -0400
Subject: [PATCH 36/38] glossary page draft complete
---
html/blog/2026-03-26-scs-residential.html | 2 +-
html/blog/2026-04-12-about.html | 2 +-
html/glossary.html | 4 +-
planzero/glossary.py | 639 +++++++++++++++++++---
4 files changed, 582 insertions(+), 65 deletions(-)
diff --git a/html/blog/2026-03-26-scs-residential.html b/html/blog/2026-03-26-scs-residential.html
index 9aad077..aece46e 100644
--- a/html/blog/2026-03-26-scs-residential.html
+++ b/html/blog/2026-03-26-scs-residential.html
@@ -16,7 +16,7 @@
{% if not blog.published %}[DRAFT] {% endif %} {{blog.title}}
- Introduction
- Residential Stationary Combustion Sources
- - An Estimator based on the National Energy Use Database
+ - Estimating Residential Stationary_Combustion_Emissions
- Critical Success Factors
- Barriers
- Potential Strategies
diff --git a/html/blog/2026-04-12-about.html b/html/blog/2026-04-12-about.html
index d046544..8539e01 100644
--- a/html/blog/2026-04-12-about.html
+++ b/html/blog/2026-04-12-about.html
@@ -150,7 +150,7 @@ Preface: Revision Control as Mechanism and Inspiration
by a loosely-organized community
of contributors, each of whom has a copy of the software
(called a {{siteref("Git Branch", "branch")|safe}},
-or {{siteref("Git Fork", "fork")|safe}} of the code), and can develop it
+or {{siteref("GitHub Fork", "fork")|safe}} of the code), and can develop it
however, and whenever they please (by
making {{siteref("Git Commit", "local commits")|safe}} of changes to their
{{siteref("Git Branch", "branch")|safe}} of source code).
diff --git a/html/glossary.html b/html/glossary.html
index 5aa7b29..a437acd 100644
--- a/html/glossary.html
+++ b/html/glossary.html
@@ -5,7 +5,7 @@
PlanZero Glossary
- Terms, acronyms, and conceptual modelling framework
+ Terms, acronyms, data sources, and the conceptual modelling framework used to implement PlanZero.