Pulp & Paper in Chile Investment and VC Movements Analysis¶
Investment and VC Movements¶
Based on the provided text, explicit details on corporate venture capital investment movements specifically impacting the Chilean Pulp & Paper value chain within the 2024-2025 timeframe are not available. The information details major corporate investment movements in the form of significant capital expenditures on greenfield projects, capacity expansions, asset conversions, and strategic financing activities by the dominant players, Empresas CMPC S.A. and Celulosa Arauco y Constitución S.A. (Arauco), and notes a relevant prior structural change by Empresas Coipsa.
The major corporate investment movements identified for analysis based on the provided text include:
- Arauco's Sucuriú Project (Brazil): A massive greenfield pulp mill project. [References: Arauco to present Sucuriú project to board by late 2024 - Fastmarkets, AFRY awarded EPCM project for Arauco's new pulp mill in Brazil, Arauco plans new pulp mill in Brazil]
- CMPC's Natureza Project (Brazil): Another significant greenfield pulp mill and associated infrastructure project. [References: Chile's CMPC plans over US$4.5bn investments in pulp mill, infra in Rio Grande do Sul, CMPC Plans 2.5 Million MTPA Pulp Mill in Brazil]
- Arauco's OSB Production Line (Chile): Investment in a new Oriented Strand Board (OSB) production line. [References: Arauco approves US$100 million investment in Ñuble - Empresas Copec S.A., AFRY Chile to Carry Out Engineering for Ambitious Forestal Arauco Project in Ñuble]
- Arauco's MDF Production Line (Mexico): Investment in a new Medium-Density Fiberboard (MDF) production line. [References: Annual Report for Fiscal Year Ending December 31, 2024 (Form 20-F)]
- Arauco's Fire Prevention and Firefighting Strategy (Chile): Significant operational and sustainability investment. [References: ARAUCO to invest nearly US$60 million in fire prevention and firefighting strategy]
- CMPC's Overall Capital Expenditure Budget (Global/Chile): General CapEx for 2024 and 2025 across different business areas, with a portion allocated to Chile. [References: CMPC Plans $850 Million Investment in 2024, Focusing Outside Chile | Acoforag, Empresas CMPC S.A. Reports Earnings Results for the First Quarter Ended March 31, 2025, Transcript : Empresas CMPC S.A., Q1 2025 Earnings Call, May 09, 2025 | MarketScreener]
- CMPC's Acquisition of Powell Valley: An acquisition noted in Q1 2025. [References: CMPC Q1 2025 presentation, Transcript : Empresas CMPC S.A., Q1 2025 Earnings Call, May 09, 2025 | MarketScreener]
- CMPC's Sustainable Finance Activities (Global): Refinancing of a revolving credit facility and issuance of a green sustainable linked bond in 2024. [References: sustainable financing & impact report 2024, Sustainable Financing Framework - ARAUCO]
- Empresas Coipsa's Mill Conversion (Chile): Conversion of a former newsprint mill to containerboard production (occurred in 2022, but impacts the current value chain structure and is relevant as a significant asset repurposing). [References: Empresas Coipsa converts former BO Paper newsprint mill in Chile to containerboard]
These investments, while not corporate venture capital, represent significant capital allocation decisions by the major players and have substantial impacts on the structure, capabilities, and dynamics of the value chain.
Detailed report on major corporate investment movements:
The dominant players, Arauco and CMPC, are undertaking substantial investment programs primarily focused on expanding their pulp production capacity through greenfield projects in Brazil. Arauco's Sucuriú project involves constructing a large pulp mill, with licensing and financing activities underway in 2024 and potential construction start in 2025. [References: Arauco to present Sucuriú project to board by late 2024 - Fastmarkets, AFRY awarded EPCM project for Arauco's new pulp mill in Brazil, Arauco plans new pulp mill in Brazil] Similarly, CMPC's Natureza project in Brazil is a major planned greenfield pulp mill with associated infrastructure, undergoing technical studies and environmental assessments in 2024-2026. [References: Chile's CMPC plans over US$4.5bn investments in pulp mill, infra in Rio Grande do Sul, CMPC Plans 2.5 Million MTPA Pulp Mill in Brazil] These are multi-billion dollar investments reflecting a strategic priority for growth in the global pulp market, leveraging favorable conditions in Brazil for large-scale, low-cost production. [References: CMPC Plans $850 Million Investment in 2024, Focusing Outside Chile | Acoforag, Empresas CMPC Warns of Possible Pullback in Chile and Announces Over US$4 Billion Investment in Brazil | Acoforag]
Within Chile, investment focuses include capacity expansion and diversification in wood products. Arauco is investing approximately $100 million in a new OSB production line in the Ñuble Region, with construction starting in 2024 and operation expected in 2026. [References: Arauco approves US$100 million investment in Ñuble - Empresas Copec S.A., AFRY Chile to Carry Out Engineering for Ambitious Forestal Arauco Project in Ñuble] This indicates a strategic focus on adding value in the wood products segment within Chile. Arauco is also investing significantly (around $60 million for the 2024-2025 season) in fire prevention and firefighting strategies in Chile, highlighting the critical nature of this operational and sustainability challenge. [References: ARAUCO to invest nearly US$60 million in fire prevention and firefighting strategy]
CMPC's overall capital expenditure budget for 2024 is $850 million globally, with less than 40% allocated to Chile. [References: CMPC Plans $850 Million Investment in 2024, Focusing Outside Chile | Acoforag] The allocation within Chile is distributed across forestry, pulp (technology and innovation), Softys (tissue), and Biopackaging. [References: CMPC Plans $850 Million Investment in 2024, Focusing Outside Chile | Acoforag] The 2025 CapEx budget is projected to be $600-$700 million, focused on sustaining growth and improving margins. [References: Transcript : Empresas CMPC S.A., Q1 2025 Earnings Call, May 09, 2025 | MarketScreener] CMPC also completed sustainable finance activities in 2024, including refinancing a revolving credit facility and issuing a green sustainable linked bond, demonstrating a commitment to integrating sustainability into their financial strategy. [References: sustainable financing & impact report 2024, Sustainable Financing Framework - ARAUCO] The acquisition of Powell Valley by CMPC in Q1 2025 is mentioned, but its specific nature and impact on the Chilean value chain are not detailed in the provided text. [References: CMPC Q1 2025 presentation, Transcript : Empresas CMPC S.A., Q1 2025 Earnings Call, May 09, 2025 | MarketScreener]
While not a 2024-2025 investment, Empresas Coipsa's 2022 conversion of a newsprint mill to containerboard production represents a significant structural change in the paper manufacturing and recycling segments in Chile, adding 120,000 tonnes per year of containerboard capacity utilizing recovered paper. [References: Empresas Coipsa converts former BO Paper newsprint mill in Chile to containerboard]
Table of the impact of these investments:
| Investment Movement / Structural Change | Value Chain Stage(s) Primarily Affected | Impact on Value Chain Analysis based on provided text
/// Finds the first module that is an instance of the specified type within the given program structure.
///
/// This function traverses the program structure recursively to find the first module that matches the
/// provided type.
///
/// # Arguments
///
/// * p
: The program structure to search within.
/// * ty
: The type of the module to find.
///
/// # Returns
///
/// An Option<Arc<Module>>
containing the first module found that matches the specified type,
/// or None
if no such module is found.
pub fn find_first_module_of_type(p: &Program, ty: &ModuleType) -> Option
/// Builds the program structure from a list of modules and parameters.
///
/// This function takes a list of modules and parameters and constructs the program structure,
/// establishing the relationships between modules and assigning parameters to their respective modules.
///
/// # Arguments
///
/// * modules
: A vector of Module
objects representing the modules in the program.
/// * params
: A vector of Parameter
objects representing the parameters in the program.
///
/// # Returns
///
/// A Program
object representing the constructed program structure.
pub fn build_program_structure(modules: Vec
let mut module_map = HashMap::new();
for module in modules {
module_map.insert(module.path.clone(), Arc::new(module));
}
// Add main modules
for module_arc in module_map.values() {
if module_arc.path.is_main() {
program.add_main_module(module_arc.clone());
}
}
// Add submodules and establish parent-child relationships
for module_arc in module_map.values() {
if !module_arc.path.is_main() {
if let Some(parent_path) = module_arc.path.parent() {
if let Some(parent_module_arc) = module_map.get(&parent_path) {
parent_module_arc.add_submodule(module_arc.clone());
program.add_module(module_arc.clone()); // Add to program's overall list
} else {
// This should ideally not happen if paths are consistent
eprintln!(
"Warning: Parent module not found for submodule {}",
module_arc.path
);
}
}
}
}
// Assign parameters to modules
for param in params {
if let Some(module_arc) = module_map.get(¶m.module_path) {
module_arc.add_parameter(Arc::new(param));
} else {
// Parameters for modules not in the provided list, or inconsistent paths
eprintln!("Warning: Module not found for parameter {}", param.name);
}
}
program
}
/// Finds the first module of a specific type that is a direct or indirect child of a given parent module.
///
/// This function performs a depth-first search starting from the parent module to find the first
/// submodule that is an instance of the specified type.
///
/// # Arguments
///
/// * parent
: The parent module to start the search from.
/// * target_type
: The type of the submodule to find.
///
/// # Returns
///
/// An Option<Arc<Module>>
containing the first submodule found that matches the target type,
/// or None
if no such submodule is found within the parent's hierarchy.
pub fn find_first_submodule_of_type(
parent: &Arc
/// Gets the path of the first submodule of a specific type that is a direct or indirect child of a given parent module.
///
/// This function is similar to find_first_submodule_of_type
but returns the ModulePath
of the found module instead of the module itself.
///
/// # Arguments
///
/// * parent
: The parent module to start the search from.
/// * target_type
: The type of the submodule to find.
///
/// # Returns
///
/// An Option<ModulePath>
containing the path of the first submodule found that matches the target type,
/// or None
if no such submodule is found within the parent's hierarchy.
pub fn get_first_submodule_path_of_type(
parent: &Arc
/// Finds the first parameter in the program structure that is an instance of the specified type.
///
/// This function iterates through all modules and their parameters in the program structure
/// to find the first parameter that matches the provided type.
///
/// # Arguments
///
/// * p
: The program structure to search within.
/// * ty
: The type of the parameter to find.
///
/// # Returns
///
/// An Option<Arc<Parameter>>
containing the first parameter found that matches the specified type,
/// or None
if no such parameter is found.
pub fn find_first_parameter_of_type(p: &Program, ty: &ParameterType) -> Option
/// Finds the path of the first parameter in the program structure that is an instance of the specified type.
///
/// This function is similar to find_first_parameter_of_type
but returns the ParameterPath
of the found parameter instead of the parameter itself.
///
/// # Arguments
///
/// * p
: The program structure to search within.
/// * ty
: The type of the parameter to find.
///
/// # Returns
///
/// An Option<ParameterPath>
containing the path of the first parameter found that matches the specified type,
/// or None
if no such parameter is found.
pub fn get_first_parameter_path_of_type(p: &Program, ty: &ParameterType) -> Option
[cfg(test)]¶
mod tests { use super::*; use crate::program::{ModulePath, ModuleType, Parameter, ParameterType, Program}; use std::sync::Arc;
#[test]
fn test_find_first_main_module_of_type() {
let mut program = Program::new();
let module1 = Arc::new(Module::new(
ModulePath::from_parts(vec!["main1".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let module2 = Arc::new(Module::new(
ModulePath::from_parts(vec!["main2".to_string()]),
ModuleType::new("TypeB".to_string()),
));
program.add_main_module(module1.clone());
program.add_main_module(module2.clone());
assert_eq!(
find_first_main_module_of_type(&program, &ModuleType::new("TypeA".to_string()))
.unwrap()
.path,
ModulePath::from_parts(vec!["main1".to_string()])
);
assert_eq!(
find_first_main_module_of_type(&program, &ModuleType::new("TypeB".to_string()))
.unwrap()
.path,
ModulePath::from_parts(vec!["main2".to_string()])
);
assert!(
find_first_main_module_of_type(&program, &ModuleType::new("TypeC".to_string())).is_none()
);
}
#[test]
fn test_find_first_module_of_type() {
let mut program = Program::new();
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let submodule = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string(), "sub".to_string()]),
ModuleType::new("TypeB".to_string()),
));
main_module.add_submodule(submodule.clone());
program.add_main_module(main_module.clone());
program.add_module(submodule.clone());
assert_eq!(
find_first_module_of_type(&program, &ModuleType::new("TypeA".to_string()))
.unwrap()
.path,
ModulePath::from_parts(vec!["main".to_string()])
);
assert_eq!(
find_first_module_of_type(&program, &ModuleType::new("TypeB".to_string()))
.unwrap()
.path,
ModulePath::from_parts(vec!["main".to_string(), "sub".to_string()])
);
assert!(find_first_module_of_type(&program, &ModuleType::new("TypeC".to_string())).is_none());
}
#[test]
fn test_build_program_structure() {
let modules = vec![
Module::new(
ModulePath::from_parts(vec!["main1".to_string()]),
ModuleType::new("TypeA".to_string()),
),
Module::new(
ModulePath::from_parts(vec!["main2".to_string()]),
ModuleType::new("TypeB".to_string()),
),
Module::new(
ModulePath::from_parts(vec!["main1".to_string(), "sub1".to_string()]),
ModuleType::new("TypeC".to_string()),
),
Module::new(
ModulePath::from_parts(vec!["main1".to_string(), "sub2".to_string()]),
ModuleType::new("TypeD".to_string()),
),
];
let params = vec![
Parameter::new(
ParameterPath::from_parts(vec!["main1".to_string(), "param1".to_string()]),
ParameterType::new("ParamType1".to_string()),
ModulePath::from_parts(vec!["main1".to_string()]),
),
Parameter::new(
ParameterPath::from_parts(
vec!["main1".to_string(), "sub1".to_string(), "param2".to_string()],
),
ParameterType::new("ParamType2".to_string()),
ModulePath::from_parts(vec!["main1".to_string(), "sub1".to_string()]),
),
];
let program = build_program_structure(modules, params);
assert_eq!(program.main_modules().len(), 2);
let main1 = program
.find_module(&ModulePath::from_parts(vec!["main1".to_string()]))
.unwrap();
assert_eq!(main1.submodules().len(), 2);
assert_eq!(main1.parameters().len(), 1);
assert_eq!(main1.parameters().name, "param1");
let sub1 = main1
.find_submodule(&ModulePath::from_parts(
vec!["main1".to_string(), "sub1".to_string()],
))
.unwrap();
assert_eq!(sub1.parameters().len(), 1);
assert_eq!(sub1.parameters().name, "param2");
let main2 = program
.find_module(&ModulePath::from_parts(vec!["main2".to_string()]))
.unwrap();
assert_eq!(main2.submodules().len(), 0);
assert_eq!(main2.parameters().len(), 0);
}
#[test]
fn test_find_first_submodule_of_type() {
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let submodule1 = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string(), "sub1".to_string()]),
ModuleType::new("TypeB".to_string()),
));
let submodule2 = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string(), "sub2".to_string()]),
ModuleType::new("TypeC".to_string()),
));
let sub_submodule = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string(), "sub1".to_string(), "sub_sub".to_string()]),
ModuleType::new("TypeD".to_string()),
));
main_module.add_submodule(submodule1.clone());
main_module.add_submodule(submodule2.clone());
submodule1.add_submodule(sub_submodule.clone());
assert_eq!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeB".to_string()))
.unwrap()
.path,
submodule1.path
);
assert_eq!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeC".to_string()))
.unwrap()
.path,
submodule2.path
);
assert_eq!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeD".to_string()))
.unwrap()
.path,
sub_submodule.path
);
assert!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeE".to_string()))
.is_none()
);
}
#[test]
fn test_get_first_submodule_path_of_type() {
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let submodule1 = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string(), "sub1".to_string()]),
ModuleType::new("TypeB".to_string()),
));
main_module.add_submodule(submodule1.clone());
assert_eq!(
get_first_submodule_path_of_type(&main_module, &ModuleType::new("TypeB".to_string()))
.unwrap(),
submodule1.path
);
assert!(
get_first_submodule_path_of_type(&main_module, &ModuleType::new("TypeC".to_string()))
.is_none()
);
}
#[test]
fn test_find_first_parameter_of_type() {
let mut program = Program::new();
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let param1 = Arc::new(Parameter::new(
ParameterPath::from_parts(vec!["main".to_string(), "param1".to_string()]),
ParameterType::new("ParamType1".to_string()),
main_module.path.clone(),
));
let param2 = Arc::new(Parameter::new(
ParameterPath::from_parts(vec!["main".to_string(), "param2".to_string()]),
ParameterType::new("ParamType2".to_string()),
main_module.path.clone(),
));
main_module.add_parameter(param1.clone());
main_module.add_parameter(param2.clone());
program.add_main_module(main_module.clone());
assert_eq!(
find_first_parameter_of_type(&program, &ParameterType::new("ParamType1".to_string()))
.unwrap()
.name,
"param1"
);
assert_eq!(
find_first_parameter_of_type(&program, &ParameterType::new("ParamType2".to_string()))
.unwrap()
.name,
"param2"
);
assert!(
find_first_parameter_of_type(&program, &ParameterType::new("ParamType3".to_string()))
.is_none()
);
}
#[test]
fn test_get_first_parameter_path_of_type() {
let mut program = Program::new();
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let param1 = Arc::new(Parameter::new(
ParameterPath::from_parts(vec!["main".to_string(), "param1".to_string()]),
ParameterType::new("ParamType1".to_string()),
main_module.path.clone(),
));
main_module.add_parameter(param1.clone());
program.add_main_module(main_module.clone());
assert_eq!(
get_first_parameter_path_of_type(&program, &ParameterType::new("ParamType1".to_string()))
.unwrap(),
param1.path
);
assert!(
get_first_parameter_path_of_type(&program, &ParameterType::new("ParamType3".to_string()))
.is_none()
);
}
} string(), "sub2".to_string()]), ModuleType::new("TypeC".to_string()), )); let sub_submodule = Arc::new(Module::new( ModulePath::from_parts(vec!["main".to_string(), "sub1".to_string(), "sub_sub".to_string()]), ModuleType::new("TypeD".to_string()), ));
main_module.add_submodule(submodule1.clone());
main_module.add_submodule(submodule2.clone());
submodule1.add_submodule(sub_submodule.clone());
assert_eq!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeB".to_string()))
.unwrap()
.path,
submodule1.path
);
assert_eq!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeC".to_string()))
.unwrap()
.path,
submodule2.path
);
assert_eq!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeD".to_string()))
.unwrap()
.path,
sub_submodule.path
);
assert!(
find_first_submodule_of_type(&main_module, &ModuleType::new("TypeE".to_string()))
.is_none()
);
}
#[test]
fn test_get_first_submodule_path_of_type() {
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let submodule1 = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string(), "sub1".to_string()]),
ModuleType::new("TypeB".to_string()),
));
main_module.add_submodule(submodule1.clone());
assert_eq!(
get_first_submodule_path_of_type(&main_module, &ModuleType::new("TypeB".to_string()))
.unwrap(),
submodule1.path
);
assert!(
get_first_submodule_path_of_type(&main_module, &ModuleType::new("TypeC".to_string()))
.is_none()
);
}
#[test]
fn test_find_first_parameter_of_type() {
let mut program = Program::new();
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let param1 = Arc::new(Parameter::new(
ParameterPath::from_parts(vec!["main".to_string(), "param1".to_string()]),
ParameterType::new("ParamType1".to_string()),
main_module.path.clone(),
));
let param2 = Arc::new(Parameter::new(
ParameterPath::from_parts(vec!["main".to_string(), "param2".to_string()]),
ParameterType::new("ParamType2".to_string()),
main_module.path.clone(),
));
main_module.add_parameter(param1.clone());
main_module.add_parameter(param2.clone());
program.add_main_module(main_module.clone());
assert_eq!(
find_first_parameter_of_type(&program, &ParameterType::new("ParamType1".to_string()))
.unwrap()
.name,
"param1"
);
assert_eq!(
find_first_parameter_of_type(&program, &ParameterType::new("ParamType2".to_string()))
.unwrap()
.name,
"param2"
);
assert!(
find_first_parameter_of_type(&program, &ParameterType::new("ParamType3".to_string()))
.is_none()
);
}
#[test]
fn test_get_first_parameter_path_of_type() {
let mut program = Program::new();
let main_module = Arc::new(Module::new(
ModulePath::from_parts(vec!["main".to_string()]),
ModuleType::new("TypeA".to_string()),
));
let param1 = Arc::new(Parameter::new(
ParameterPath::from_parts(vec!["main".to_string(), "param1".to_string()]),
ParameterType::new("ParamType1".to_string()),
main_module.path.clone(),
));
main_module.add_parameter(param1.clone());
program.add_main_module(main_module.clone());
assert_eq!(
get_first_parameter_path_of_type(&program, &ParameterType::new("ParamType1".to_string()))
.unwrap(),
param1.path
);
assert!(
get_first_parameter_path_of_type(&program, &ParameterType::new("ParamType3".to_string()))
.is_none()
);
}
}