An error occurred while processing the template.
The following has evaluated to null or missing:
==> .vars['reserved-article-group-id']  [in template "161555#161602#ARTICLE-PAGE---HERO" at line 14, column 20]

----
Tip: It's the final [] step that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
	- Failed at: #assign groupId = .vars["reserved-art...  [in template "161555#161602#ARTICLE-PAGE---HERO" at line 14, column 1]
----
1<#assign namespace = randomNamespace /> 
2<#assign JSONFactoryUtil = staticUtil["com.liferay.portal.kernel.json.JSONFactoryUtil"] /> 
3<#assign journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService") /> 
4<#assign containerId = randomNamespace /> 
5<#assign shareURL = themeDisplay.getPortalURL()+themeDisplay.getURLCurrent()/> 
6 
7<#-- Auto-assign ALL category to every article --> 
8<#assign assetCategoryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetCategoryLocalService") /> 
9<#assign assetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") /> 
10<#assign journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService") /> 
11  
12<#-- Get current JournalArticle --> 
13<#assign articleId = .vars['reserved-article-id'].data /> 
14<#assign groupId = .vars['reserved-article-group-id'].data?number /> 
15<#assign journalArticle = journalArticleLocalService.getArticle(groupId, articleId) /> 
16  
17<#-- Get AssetEntry for this article --> 
18<#assign assetEntry = assetEntryLocalService.getEntry("com.liferay.journal.model.JournalArticle", journalArticle.getResourcePrimKey()) /> 
19  
20<#-- Your ALL category ID --> 
21<#assign allCategoryId = 4614994 /> 
22  
23<#-- Assign ALL category if not already set --> 
24<#if !assetEntry.getCategories()?seq_contains(assetCategoryLocalService.getCategory(allCategoryId))> 
25    <#assign categoryIds = assetEntry.getCategoryIds() + [allCategoryId] /> 
26    ${assetEntryLocalService.updateEntry( 
27        assetEntry.getUserId(), 
28        assetEntry.getGroupId(), 
29        assetEntry.getClassName(), 
30        assetEntry.getClassPK(), 
31        assetEntry.getUuid(), 
32        assetEntry.getClassTypeId(), 
33        categoryIds, 
34        assetEntry.getTagNames() 
35    )} 
36</#if> 
37 
38<div class="hero jmarticle__hero-main-container"> 
39    <div class="container"> 
40        <div class="row"> 
41            <div class="article__cont"> 
42                <h1 class="article__heading">${Title.getData()}</h1> 
43                <#if (SubTitle.getData())??> 
44                    <p class="article__para"> 
45                        ${SubTitle.getData()} 
46                    </p> 
47                </#if> 
48 
49                <#assign Date_Data=getterUtil.getString(Date.getData())> 
50                <#if validator.isNotNull(Date_Data)> 
51                    <p class="article__date"> 
52                        <#assign Date_DateObj=dateUtil.parseDate("yyyy-MM-dd", Date_Data, locale)> 
53                        ${dateUtil.getDate(Date_DateObj, "dd MMMM yyyy", locale)} 
54                    </p> 
55                </#if> 
56 
57                <#if Link_Title?? && Link_Title.getData()?has_content> 
58                    <#list Link_Title.getSiblings() as cur_Link_Title> 
59						<#if cur_Link_Title?? && cur_Link_Title.getData()?has_content> 
60							<#assign linkHref="#" /> 
61							<#assign target="" /> 
62							 
63							<#assign rowId = containerId + cur_Link_Title?index /> 
64							<#if cur_Link_Title.Link_Internal?? && cur_Link_Title.Link_Internal.getFriendlyUrl()?has_content> 
65								<#assign linkHref=cur_Link_Title.Link_Internal.getFriendlyUrl() /> 
66							<#elseif cur_Link_Title.Link_Media?? && cur_Link_Title.Link_Media.getData()?has_content> 
67								<#assign linkHref=cur_Link_Title.Link_Media.getData() /> 
68							<#elseif cur_Link_Title.Link_Mailto?? && cur_Link_Title.Link_Mailto.getData()?has_content> 
69								<#assign linkHref="mailto:" + cur_Link_Title.Link_Mailto.getData() /> 
70							<#elseif cur_Link_Title.Link_External?? && cur_Link_Title.Link_External.getData()?has_content> 
71								<#assign linkHref=cur_Link_Title.Link_External.getData() /> 
72							</#if> 
73							<#if (cur_Link_Title.Link_Target??) && (cur_Link_Title.Link_Target.getData())?has_content> 
74								<#assign target=cur_Link_Title.Link_Target.getData() /> 
75							</#if> 
76							<div id="blue_button_click_div" data-analytics-asset-type="custom"> 
77								<div class="jmarticle__hero-btn-list"> 
78									<a id="panel_blue_button_${rowId}" class="article__btn" href="${linkHref}" title="${cur_Link_Title.getData()}" target="${target}" data-analytics-asset-action="click" onclick="onBlueButtonClick(this)" > 
79										<span class="btn__text">${cur_Link_Title.getData()}</span> 
80										<span class="arrow__forward"></span> 
81									</a> 
82								</div> 
83							</div> 
84						</#if> 
85					</#list> 
86                </#if> 
87				  <div class="article-download-share"> 
88                
89                    <#if PDFTitle?? && PDFTitle.getData()?has_content> 
90                        <#assign title = PDFTitle.getData() /> 
91                        <#assign pdf_link = "#" /> 
92                        <#assign pdfrowId = containerId /> 
93                        <#if PDFTitle.PDF?? && PDFTitle.PDF.getData()?has_content> 
94                            <#assign pdf_link = PDFTitle.PDF.getData() /> 
95                        </#if> 
96                        <div class="article-download"> 
97                            <div id="blue_button_click_div" data-analytics-asset-type="custom"> 
98                                <div class="jmarticle__hero-btn-list"> 
99                                    <a id="panel_blue_button_${pdfrowId}" class="article__btn" href="${pdf_link}" title="${title}" data-analytics-asset-action="click" onclick="onBlueButtonClick(this)"> 
100                                        <span class="btn__text">${title}</span> 
101                                        <span class="arrow__forward"></span> 
102                                    </a> 
103                                   <a id="panel_blue_button_${pdfrowId}" class="article__btn-mobile" href="${pdf_link}"             title="${title}" data-analytics-asset-action="click" onclick="onBlueButtonClick(this)"> 
104                                       <img src="${themeDisplay.getPathThemeImages()}/new__jm__images/download-white-icon.svg" alt="twitter image"/> 
105                                    </a> 
106                                </div> 
107                            </div> 
108                        </div> 
109                    </#if> 
110                                 
111                </div> 
112                 
113            </div> 
114        </div> 
115       <#if (ImageLarge.getData())?? && ImageLarge.getData() != ""> 
116            <div class="row jmarticle__hero-banner" id="jmarticle__hero-${namespace}"  
117            style="background-image: url(${ImageLarge.getData()})"> 
118        </#if> 
119 
120        </div> 
121    </div> 
122</div> 
123 
124<#if Author?? && Author.getData()?has_content> 
125    <div class="jmarticle__author-info"> 
126        <div class="container"> 
127            <div class="hero__bar"> 
128                     <#list Author.getSiblings() as cur_Author> 
129                            <#if cur_Author?? && cur_Author.getData()?has_content> 
130                                <#assign 
131                                    webContentData = jsonFactoryUtil.createJSONObject(cur_Author.getData()) 
132                                    gId = groupId 
133                                    journalArticle = journalArticleLocalService.getArticleByUrlTitle(gId, webContentData.title) 
134                                    docXML = saxReaderUtil.read(journalArticle.getContent()) 
135 
136                                    fName = docXML.valueOf("//dynamic-element[@name='FullName']/dynamic-content/text()") 
137                                    jobTitle = docXML.valueOf("//dynamic-element[@name='JobTitle']/dynamic-content/text()") 
138                                    role = docXML.valueOf("//dynamic-element[@name='ContactInfo']/dynamic-content/text()") 
139                                    photoXML = docXML.valueOf("//dynamic-element[@name='Photo']/dynamic-content/text()") 
140                                    photoSrc = JSONFactoryUtil.createJSONObject(photoXML).getString("uuid") 
141                                    fullName = docXML.valueOf("//dynamic-element[@name='Title']/dynamic-content/text()") 
142                                    authorImageXML = docXML.valueOf("//dynamic-element[@name='ImageLarge']/dynamic-content/text()") 
143                                    authImageSrc = JSONFactoryUtil.createJSONObject(authorImageXML).getString("uuid") 
144                                    summary = docXML.valueOf("//dynamic-element[@name='Summary']/dynamic-content/text()") 
145                                /> 
146 
147                                    <div class="author"> 
148                                        <figure class="author__image"> 
149                                            <#if authImageSrc?? && authImageSrc?has_content> 
150                                                <img src="/documents/${groupId}/${authImageSrc}" alt="${fullName}" class="author_img_class_new"> 
151                                            <#elseif photoSrc?? && photoSrc?has_content> 
152                                                <img src="/documents/${groupId}/${photoSrc}" alt="${fName}" class="author_img_class_new"> 
153                                            </#if> 
154                                        </figure> 
155                                        <div class="author__details d-flex flex-column"> 
156                                            <h3 class="author__name"> 
157                                                <a rel="author"> 
158                                                    <#if fullName?? && fullName?has_content> 
159                                                        ${fullName} 
160                                                    <#elseif fName?? && fName?has_content> 
161                                                        ${fName} 
162                                                    </#if> 
163                                                </a> 
164                                            </h3> 
165                                            <p class="author__desc text-size-a author_job_title"> 
166                                                <#if jobTitle?? && jobTitle?has_content> 
167                                                    ${jobTitle} 
168                                                </#if> 
169                                            </p> 
170                                            <span> 
171                                                <p class="author__desc text-size-a"> 
172                                                    <#if summary?? && summary?has_content> 
173                                                        ${summary} 
174                                                    </#if> 
175                                                    <#if role?? && role?has_content> 
176                                                        ${role} 
177                                                    </#if> 
178                                                </p> 
179                                            </span> 
180                                        </div> 
181                                        <div> 
182                                            <div class="author__contact"> 
183                                                <ul class="author__contact-list"> 
184                                                </ul> 
185                                            </div> 
186                                        </div> 
187                                    </div> 
188                             </#if> 
189                     </#list> 
190            </div> 
191    	</div> 
192    </div> 
193</#if> 
194 
195 
196 
197<div class="container"> 
198    <div class="row"> 
199        <div class="article__intro"> 
200            <#if (Introduction.getData())??> 
201            	${Introduction.getData()} 
202            </#if> 
203 
204           <div class="article-download-share"> 
205                
206                    <#if PDFTitle?? && PDFTitle.getData()?has_content> 
207                        <#assign title = PDFTitle.getData() /> 
208                        <#assign pdf_link = "#" /> 
209                        <#assign pdfrowId = containerId /> 
210                        <#if PDFTitle.PDF?? && PDFTitle.PDF.getData()?has_content> 
211                            <#assign pdf_link = PDFTitle.PDF.getData() /> 
212                        </#if> 
213                        <div class="article-download"> 
214                            <div id="blue_button_click_div" data-analytics-asset-type="custom"> 
215                                <div class="jmarticle__hero-btn-list"> 
216                                    <a id="panel_blue_button_${pdfrowId}" class="article__btn" href="${pdf_link}" title="${title}" data-analytics-asset-action="click" onclick="onBlueButtonClick(this)"> 
217                                        <span class="btn__text">${title}</span> 
218                                        <span class="arrow__forward"></span> 
219                                    </a> 
220                                   <a id="panel_blue_button_${pdfrowId}" class="article__btn-mobile" href="${pdf_link}"             title="${title}" data-analytics-asset-action="click" onclick="onBlueButtonClick(this)"> 
221                                       <img src="${themeDisplay.getPathThemeImages()}/new__jm__images/download-white-icon.svg" alt="twitter image"/> 
222                                    </a> 
223                                </div> 
224                            </div> 
225                        </div> 
226                    </#if>               
227                
228                   
229               <div class="article-share"> 
230                   <a href="https://linkedin.com/shareArticle?url=${shareURL}&title=<#if (Title.getData())??>${Title.getData()}</#if>" target="_blank"> 
231                       <img class="socialicons__img" src="${themeDisplay.getPathThemeImages()}/new__jm__images/social_icons-LinkedIN.png" alt="twitter image"/> 
232                    </a> 
233                      <a  href="https://twitter.com/share?url=${shareURL}&text=<#if (Title.getData())??>${Title.getData()}</#if>" target="_blank"> 
234                       <img class="socialicons__img" src="${themeDisplay.getPathThemeImages()}/new__jm__images/social_icons-Twitter.png" alt="twitter image"/> 
235                    </a> 
236                </div> 
237            </div> 
238       
239        </div> 
240    </div> 
241</div> 
242 
243<style> 
244    img.author_img_class_new { 
245        width: 100% !important; 
246        height: 100% !important; 
247        position: absolute; 
248        margin: auto; 
249        top: 0; 
250        bottom: 0; 
251
252     
253    .article__btn:focus-visible { 
254    outline: 2px solid blue !important; 
255
256 
257@media (min-width: 320px) and (max-width: 650px) { 
258	.article__intro li { 
259	   margin-left: -32px !important;		 
260
261	.journal-content-article .container .article__intro { 
262    margin-top: 22px !important; 
263
264
265@media (min-width: 540px) and (max-width: 834px) { 
266    .article__intro li { 
267	      margin-left: -32px !important;		 
268
269    .article__intro { 
270        margin: 60px 15px 0 5px !important;         
271
272
273  
274@media (min-width: 320px) and (max-width: 539px){ 
275.jmarticle__author-info { 
276    padding: 0px 0; 
277
278.jmarticle__author-info .author__image { 
279    width: 39px; 
280    height: 40px; 
281
282 
283.jmarticle__author-info .author__name a { 
284    line-height: 0px; 
285    font-size: 18px !important; 
286    margin-right: 10px; 
287
288 
289.jmarticle__author-info .author__desc { 
290     
291    line-height: 14px; 
292    font-size: 12px !important; 
293    margin-bottom: 7px; 
294
295
296  
297</style> 

As we move towards a cleaner and more sustainable future, platinum group metal (PGM) catalysed technologies are gaining increased focus. They are an asset to the net zero transition and allow reliable and sustainable growth of fuel cells, but this is not yet fully recognised by the market and regulators, with debate on their availability, cost and circularity.

PGMs are used in the performance-defining components at the heart of proton exchange membrane (PEM) fuel cells. Platinum in particular is essential to making this technology work. It catalyses the reaction of the hydrogen fuel with oxygen to produce power, with water and heat being formed as by-products. And it is unique in its ability to withstand the acidic and oxidising environment of a fuel cell stack while sustaining high catalytic activity over the stack’s lifetime.

These properties also make platinum irreplaceable elsewhere in the fuel cell stack. It protects vital components from the harsh conditions to reduce degradation but still maintains conductivity of the electricity generated.

Although PGMs are expensive metals, they make a relatively small contribution (estimated at less than 5%) to the overall cost of a fuel cell vehicle, as they are used in relatively small quantities. As is typical with PGMs, they are highly metal efficient with a little going a long way, and research and development efforts are continually working to further reduce the metal needed to maintain performance.

The value of PGMs in fuel cells does, however, incentivise their recovery and recycling. Platinum remains in a recoverable form once the fuel cell stack reaches the end of its useful life, and once recycled, PGMs have exactly the same properties as when they are first mined. This means primary and secondary platinum are completely interchangeable and can be reused repeatedly in new stacks. In the context of circularity, the cost of PGMs should be viewed as an investment and an asset to owners.

A diverse and robust platinum supply

Platinum benefits from long‑standing supply chains, with around 65% of supply coming from well‑established mines in southern Africa, operated almost exclusively by large, publicly listed mining companies with exceptional environmental, social, and governance (ESG) profiles. As well as having substantial above‑ground stocks, around 20-25% of platinum currently supplied to the market is from ‘open-loop’ recycling, giving a diverse source of supply and playing a crucial role in meeting demand.

And this is far from the total amount of platinum recycled. PGMs are also routinely recycled in a ‘closed loop’, where the original purchaser of the metal retains ownership during the recycling process for reuse in the same application. So, while closed-loop recycled metal is not available to the market as supply, it greatly reduces the amount of “replacement” metal needed and benefits from significantly lower carbon intensity compared to primary mining.

On the demand side, platinum’s current largest market is catalytic converters for new vehicles, which will steadily decline as the internal combustion engine (ICE) is phased out. Its use in jewellery has also been declining for the last decade. Considering this changing demand profile along with the robust supply from primary mining, open-loop recycling and above-ground stocks, platinum is well positioned for the growing fuel cell market – if it gains the policy support it needs.

What’s more, platinum’s use in fuel cell vehicles is critical to iridium supply for electrolysers. PGMs are never mined in isolation and more than 90% of global iridium is produced as a minor by‑product of platinum mining in southern Africa. Iridium only occurs in trace quantities in the ore, with a total of around seven tonnes mined in 2023, compared to around 180 tonnes of platinum. So iridium will never be mined in its own right, nor will it singly justify the business case for future platinum mining investments. It is only with a healthy replacement demand for platinum in fuel cell vehicles that continued mining of platinum will be incentivised in South Africa and Zimbabwe, consequently securing iridium supply for electrolytic (green) hydrogen.

Well-established PGM recycling network

Value-driven recovery and refining of PGMs from their vast range of industrial applications has been widely taking place for decades. As a result, platinum already benefits from a well-established global recycling network and significant existing recycling capacity: several large refineries process platinum sourced from across the world for reuse by customers or for sale to the market.

Recycling of PGMs is routine, but that does not mean it is easy. The technical and commercial complexity of processing these metals has led to specialist secondary PGM refiners serving the global market from large operations in centralised locations. Each PGM refiner has optimised different capabilities and typically targets different types of material. As a result, PGM recycling is far more optimised and cost-effective than if numerous individual companies were to process and recycle PGMs from the variety of industrial uses within their own domestic boundaries.

The key exception is China, which is a closed recycling market, in that PGMcontaining spent material arising in China must be recycled in China, and therefore serves its domestic requirement.

For the rest of the world, the salient feature of PGM recycling networks is their global nature: PGM ingot, sponge (powder), catalysts, products, end-of-life material and the resulting refined metal routinely cross borders. This is a normal and important part of the PGM supply chains. As a result, metal that is first sold in a particular region is not necessarily used, recovered, processed, or resold in that same region, and metal can quite often be located in a different region from its owner.

Decades of PGM recycling experience shows the benefits of open borders to achieving efficient circularity. Policy measures that are looking to build up domestic supply chain capability for ‘critical’ or ‘strategic’ raw materials, which usually include PGMs, need to consider that the same challenges do not exist as they do for other critical materials, such as battery metals and rare-earth elements. The mature and global infrastructure for PGMs is unique among critical metals and tailored policy is needed to capitalise on this setup.

Optimising the unique circularity of PGMs

In theory, the recyclability of PGMs is very high – if effectively recovered from the application, nearly all the metal can be reused. But in practice, significant losses of PGMs are seen today, overwhelmingly due to inefficient collection practices in open-loop recycling, driving less than optimal recycling rates. In contrast, closed-loop recycling tends to minimise collection losses, and is a well-established model in the PGM industry.

If continued investment in refining facilities by established PGM recyclers is supported, there will be sufficient and optimised secondary PGM refining capacity for fuel cell recycling. Regulatory measures to support PGM recycling should therefore focus on mandating producer responsibility or other measures that incentivise collection of all spent material containing PGMs at end of life and ensuring the material enters the established global recycling infrastructure.

The closed-loop model can be taken one step further to build in an end-of-life solution at the very start of the fuel cell life. ‘Design to refine’ approaches are gaining increasing traction in the PGM industry, seeing recyclers working with OEMs and technology providers during product development to ensure the PGM they use today will have a known route to recovery tomorrow.

This does not mean that technology choices need be constrained by recycling considerations. Quite often a collaboration with a specialist PGM refiner can see a ‘refine for design’ approach instead. If refiners understand technology developments that will give rise to a novel scrap material in future, they can start to develop and optimise processes to ensure this material can be recycled efficiently.

This has recently been exemplified by Johnson Matthey’s HyRefine technology – a world-first lab scale demonstration that recycles both the PGMs and the valuable membrane ionomer from fuel cell and electrolyser materials. New paths to recycling both PGMs and ionomer with purely chemical processes offer a significantly lower carbon footprint to conventional recycling routes, using less energy and water while producing less waste.

Conclusion

There is no doubt that circularity for platinum (and other PGMs) in fuel cell technologies will be established, leveraging the existing global recycling network and incentivised by the value of the metal and lower carbon intensity compared to primary metal. The remaining challenge will be ensuring collection and refining processes are optimised for these new materials to maximise metal recovery – a challenge that is being actively addressed by the industry.

PGM supply chains and recycling work today and they work well. But to ensure robust and efficient recycling networks for the future, regulatory measures should be tailored to support and optimise what already exists. Capitalising on the mature and established ecosystem for these metals will enable a reliable, sustainable future for fuel cells.

This article was originally published in H2 View.

Explore our products and services

Products and services

PGM recycling and refining

Explore
Market

Fuel cells

Read more