Skip to content

Commit

Permalink
Merge pull request #527 from NREL/windows_intructions
Browse files Browse the repository at this point in the history
Update venv instructions in README for windows
  • Loading branch information
calbaker authored Jun 27, 2024
2 parents 91a2fca + 218f86a commit 77cf2a8
Show file tree
Hide file tree
Showing 16 changed files with 95 additions and 30 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

![Model Framework Schematic](https://raw.githubusercontent.com/NREL/altrios/main/.github/images/ALTRIOS_schematic_Alfred_Hicks.png)

The Advanced Locomotive Technology and Rail Infrastructure Optimization System ([ALTRIOS](https://www.nrel.gov/transportation/altrios.html)) is a unique, fully integrated, open-source software tool to evaluate strategies for deploying advanced locomotive technologies and associated infrastructure for cost-effective decarbonization. ALTRIOS simulates freight-demand driven train scheduling, mainline meet-pass planning, locomotive dynamics, train dynamics, energy conversion efficiencies, and energy storage dynamics of line-haul train operations. Because new locomotives represent a significant long-term capital investment and new technologies must be thoroughly demonstrated before deployment, this tool provides guidance on the risk/reward tradeoffs of different technology rollout strategies. An open, integrated simulation tool is invaluable for identifying future research needs and making decisions on technology development, routes, and train selection. ALTRIOS was developed as part of a collaborative effort by a team comprising The National Renewable Energy Laboratory (NREL), University of Illinois Urbana-Champaign (UIUC), Southwest Research Institute (SwRI), and BNSF Railway.
The Advanced Locomotive Technology and Rail Infrastructure Optimization System ([ALTRIOS](https://www.nrel.gov/transportation/altrios.html)) is a unique, fully integrated, open-source software tool to evaluate strategies for deploying advanced locomotive technologies and associated infrastructure for cost-effective decarbonization. ALTRIOS simulates freight-demand driven train scheduling, mainline meet-pass planning, locomotive dynamics, train dynamics, energy conversion efficiencies, and energy storage dynamics of line-haul train operations. Because new locomotives represent a significant long-term capital investment and new technologies must be thoroughly demonstrated before deployment, this tool provides guidance on the risk/reward tradeoffs of different technology rollout strategies. An open, integrated simulation tool is invaluable for identifying future research needs and making decisions on technology development, routes, and train selection. ALTRIOS was developed as part of a collaborative effort by a team comprising The National Renewable Energy Laboratory (NREL), University of Texas (UT), Southwest Research Institute (SwRI), and BNSF Railway.

Much of the core code in ALTRIOS is written in the [Rust Programming Language](https://www.rust-lang.org/) to ensure excellent computational performance and robustness, but we've built ALTRIOS with the intent of users interacting with the code through our feature-rich [Python](https://www.python.org/) interface.

Expand All @@ -22,14 +22,19 @@ If you are an ALTRIOS developer, see [Developer Documentation](https://nrel.gith
- Option 2 -- Anaconda: we recommend https://docs.conda.io/en/latest/miniconda.html.
1. Setup a python environment. ALTRIOS can work with Python 3.9, or 3.10, but we recommend 3.10 for better performance and user experience. Create a python environment for ALTRIOS with either of two methods:
- Option 1 -- [Python Venv](https://docs.python.org/3/library/venv.html)
1. Navigate to your project folder in which you'd like to store model data and run ALTRIOS.
1. Assuming you have Python 3.10 installed, run `python3.10 -m venv altrios-venv` in your terminal enviroment (we recommend PowerShell in Windows, which comes pre-installed). This tells Python 3.10 to use the `venv` module to create a virtual environment (which will be ignored by git if named `altrios-venv`) in the `ALTRIOS/altrios-venv/`.
1. Navigate to the ALTRIOS folder you just cloned or any folder you'd like for using ALTRIOS. Remember the folder you use!
1. Assuming you have Python 3.10 installed, run
- `(path to your python3.10 e.g. ~/AppData/Local/Programs/Python/Python310/python.exe) -m venv altrios-venv` in Windows
- `python3.10 -m venv altrios-venv` in Mac/Unix/Linux
in your terminal enviroment (we recommend PowerShell in Windows, which comes pre-installed).

This tells Python 3.10 to use the `venv` module to create a virtual environment (which will be ignored by git if named `altrios-venv`) in the `ALTRIOS/altrios-venv/`.
1. Activate the environment you just created to install packages or anytime you're running ALTRIOS:
- Mac and Linux: `source altrios-venv/bin/activate`
- Windows: `altrios-venv/Scripts/activate.bat` in a windows command prompt or power shell or `source ./altrios-venv/scripts/activate` in git bash terminal
- Windows: `altrios-venv/Scripts/activate.bat` in a windows command prompt or power shell or `source altrios-venv/Scripts/activate` in git bash terminal
- When the environment is activated, your terminal session will have a decorator that looks like `(altrios-venv)`.
- Option 2 -- Anaconda:
1. Open an Anaconda prompt (in Windows, we recommend Anaconda Powershell Prompt) and run the command `conda create -n altrios python=3.10` to create an Anaconda environment named `altrios`.
1. Open an Anaconda prompt (in Windows, we recommend _Anaconda_ Powershell Prompt) and run the command `conda create -n altrios python=3.10` to create an Anaconda environment named `altrios`.
1. Activate the environment to install packages or anytime you're running ALTRIOS: run `conda activate altrios`.

### ALTRIOS Setup
Expand Down
4 changes: 1 addition & 3 deletions build_and_test.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# assumes a python environment has been created and activated
echo "Testing rust" && \
(cd rust/ && cargo test --workspace) && \
# pip install -qe ".[dev]" && \
# assumes `pip install -qe ".[dev]"` has been run already
echo "Building python API" && \
maturin develop --release && \
pip install -qe ".[dev]" && \
echo "Running python tests" && \
pytest -v python/altrios/tests && \
echo "Verifying that demos run" && \
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ classifiers = [
]
dependencies = [
"pandas>=2",
"numpy",
"numpy==1.24",
"pymoo==0.6",
"openpyxl",
"xlrd",
Expand Down
10 changes: 7 additions & 3 deletions rust/altrios-core/src/consist/consist_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl Consist {
|f_sum, (i, loco)| -> anyhow::Result<si::Force> {
Ok(loco
.force_max()?
.ok_or_else(|| anyhow!("Locomotive {i} does not have `force_max` set"))?
.with_context(|| anyhow!("Locomotive {i} does not have `force_max` set"))?
+ f_sum)
},
)
Expand Down Expand Up @@ -331,7 +331,11 @@ impl Consist {
// maybe put logic for toggling `engine_on` here

for (i, (loco, pwr_out)) in self.loco_vec.iter_mut().zip(pwr_out_vec.iter()).enumerate() {
log::info!("Solving locomotive #{}", i);
log::info!(
"Solving locomotive #{}\n`pwr_out: `{} MW",
i,
pwr_out.get::<si::megawatt>().format_eng(None)
);
loco.solve_energy_consumption(*pwr_out, dt, engine_on)
.map_err(|err| {
err.context(format!(
Expand Down Expand Up @@ -500,7 +504,7 @@ impl Mass for Consist {
|m_acc, (i, loco)| -> anyhow::Result<si::Mass> {
let loco_mass = loco
.mass()?
.ok_or_else(|| anyhow!("Locomotive {i} does not have `mass` set"))?;
.with_context(|| anyhow!("Locomotive {i} does not have `mass` set"))?;
let new_mass: si::Mass = loco_mass + m_acc;
Ok(new_mass)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl LocoTrait for BatteryElectricLoco {
) -> anyhow::Result<()> {
// TODO: proposed interface location to feed in the catenary
self.res.set_cur_pwr_out_max(
pwr_aux.ok_or(anyhow!(format_dbg!("`pwr_aux` not provided")))?,
pwr_aux.with_context(|| anyhow!(format_dbg!("`pwr_aux` not provided")))?,
None,
None,
)?;
Expand Down
2 changes: 1 addition & 1 deletion rust/altrios-core/src/consist/locomotive/hybrid_loco.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl LocoTrait for Box<HybridLoco> {
dt: si::Time,
) -> anyhow::Result<()> {
self.res.set_cur_pwr_out_max(
pwr_aux.ok_or(anyhow!(format_dbg!("`pwr_aux` not provided")))?,
pwr_aux.with_context(|| anyhow!(format_dbg!("`pwr_aux` not provided")))?,
None,
None,
)?;
Expand Down
9 changes: 5 additions & 4 deletions rust/altrios-core/src/consist/locomotive/locomotive_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@ impl LocoParams {
fn from_hash(mut params: HashMap<&str, f64>) -> anyhow::Result<Self> {
let pwr_aux_offset_watts = params
.remove("pwr_aux_offset_watts")
.ok_or_else(|| anyhow!("Must provide 'pwr_aux_offset_watts'."))?;
.with_context(|| anyhow!("Must provide 'pwr_aux_offset_watts'."))?;
let pwr_aux_traction_coeff_ratio = params
.remove("pwr_aux_traction_coeff_ratio")
.ok_or_else(|| anyhow!("Must provide 'pwr_aux_traction_coeff_ratio'."))?;
.with_context(|| anyhow!("Must provide 'pwr_aux_traction_coeff_ratio'."))?;
let force_max_newtons = params
.remove("force_max_newtons")
.ok_or_else(|| anyhow!("Must provide 'force_max_newtons'."))?;
.with_context(|| anyhow!("Must provide 'force_max_newtons'."))?;
let mass_kg = params.remove("mass_kg");
ensure!(
params.is_empty(),
Expand Down Expand Up @@ -655,7 +655,8 @@ impl Locomotive {
}

pub fn force_max(&self) -> anyhow::Result<Option<si::Force>> {
self.check_force_max()?;
self.check_force_max()
.with_context(|| anyhow!(format_dbg!()))?;
Ok(self.force_max)
}

Expand Down
6 changes: 6 additions & 0 deletions rust/altrios-core/src/meet_pass/est_times/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ pub fn make_est_times<N: AsRef<[Link]>>(
let time_depart = speed_limit_train_sim.state.time;

// Push initial fake nodes
log::debug!("{}", format_dbg!("Push initial fake nodes."));
est_times.push(EstTime {
idx_next: 1,
..Default::default()
Expand All @@ -502,6 +503,7 @@ pub fn make_est_times<N: AsRef<[Link]>>(
});

// Add origin estimated times
log::debug!("{}", format_dbg!("Add origin estimated times."));
for orig in origs {
ensure!(
orig.offset == si::Length::ZERO,
Expand All @@ -520,6 +522,7 @@ pub fn make_est_times<N: AsRef<[Link]>>(
..Default::default()
};

log::debug!("{}", format_dbg!());
insert_est_time(
&mut est_times,
&mut est_alt,
Expand All @@ -535,6 +538,7 @@ pub fn make_est_times<N: AsRef<[Link]>>(
..Default::default()
},
);
log::debug!("{}", format_dbg!());
insert_est_time(
&mut est_times,
&mut est_alt,
Expand Down Expand Up @@ -565,6 +569,7 @@ pub fn make_est_times<N: AsRef<[Link]>>(
}

// Fix distances for different origins
log::debug!("{}", format_dbg!("Fix distances for different origins"));
{
let mut est_idx_fix = 1;
while est_idx_fix != EST_IDX_NA {
Expand All @@ -580,6 +585,7 @@ pub fn make_est_times<N: AsRef<[Link]>>(
let mut est_idxs_end = Vec::<EstIdx>::with_capacity(8);

// Iterate and process all saved sims
log::debug!("{}", format_dbg!("Iterate and process all saved sims"));
while let Some(mut sim) = saved_sims.pop() {
let mut has_split = false;
ensure!(
Expand Down
10 changes: 9 additions & 1 deletion rust/altrios-core/src/track/link/elev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ impl ObjState for [Elev] {
errors.push(anyhow!("There must be at least two elevations!"));
}
if !self.windows(2).all(|w| w[0].offset < w[1].offset) {
errors.push(anyhow!("Offsets must be sorted and unique!"));
let err_pairs: Vec<Vec<si::Length>> = self
.windows(2)
.filter(|w| w[0].offset >= w[1].offset)
.map(|w| vec![w[0].offset, w[1].offset])
.collect();
errors.push(anyhow!(
"Offsets must be sorted and unique! Invalid offsets: {:?}",
err_pairs
));
}
errors.make_err()
}
Expand Down
10 changes: 9 additions & 1 deletion rust/altrios-core/src/track/link/heading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,15 @@ impl ObjState for [Heading] {
errors.push(anyhow!("There must be at least two headings!"));
}
if !self.windows(2).all(|w| w[0].offset < w[1].offset) {
errors.push(anyhow!("Offsets must be sorted and unique!"));
let err_pairs: Vec<Vec<si::Length>> = self
.windows(2)
.filter(|w| w[0].offset >= w[1].offset)
.map(|w| vec![w[0].offset, w[1].offset])
.collect();
errors.push(anyhow!(
"Offsets must be sorted and unique! Invalid offsets: {:?}",
err_pairs
));
}
errors.make_err()
}
Expand Down
16 changes: 11 additions & 5 deletions rust/altrios-core/src/track/link/link_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ impl Link {
self.speed_set = Some(
self.speed_sets
.get(&train_type)
.ok_or(anyhow!(
"No value found for train_type: {:?} in `speed_sets`.",
train_type
))?
.with_context(|| {
anyhow!(
"No value found for train_type: {:?} in `speed_sets`.",
train_type
)
})?
.clone(),
);
self.speed_sets = HashMap::new();
Expand Down Expand Up @@ -323,7 +325,11 @@ impl SerdeAPI for Network {
})?;
let mut network = match Self::from_reader(file, extension) {
Ok(network) => network,
Err(err) => NetworkOld::from_file(filepath).with_context(|| err)?.into(),
Err(err) => NetworkOld::from_file(filepath)
.map_err(|old_err| {
anyhow!("\nattempting to load as `Network`:\n{}\nattempting to load as `NetworkOld`:\n{}", err, old_err)
})?
.into(),
};
network.init()?;

Expand Down
10 changes: 9 additions & 1 deletion rust/altrios-core/src/track/path_track/link_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,15 @@ impl ObjState for [LinkPoint] {
}

if !self.windows(2).all(|w| w[0].offset < w[1].offset) {
errors.push(anyhow!("Link point offsets must be sorted and unique!"));
let err_pairs: Vec<Vec<si::Length>> = self
.windows(2)
.filter(|w| w[0].offset >= w[1].offset)
.map(|w| vec![w[0].offset, w[1].offset])
.collect();
errors.push(anyhow!(
"Link point offsets must be sorted and unique! Invalid offsets: {:?}",
err_pairs
));
}

errors.make_err()
Expand Down
10 changes: 9 additions & 1 deletion rust/altrios-core/src/track/path_track/path_res_coeff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,15 @@ impl ObjState for [PathResCoeff] {
errors.push(anyhow!("There must be at least two path res coeffs!"));
}
if !self.windows(2).all(|w| w[0].offset < w[1].offset) {
errors.push(anyhow!("Offsets must be sorted and unique!"));
let err_pairs: Vec<Vec<si::Length>> = self
.windows(2)
.filter(|w| w[0].offset >= w[1].offset)
.map(|w| vec![w[0].offset, w[1].offset])
.collect();
errors.push(anyhow!(
"Offsets must be sorted and unique! Invalid offsets: {:?}",
err_pairs
));
}
if !self
.windows(2)
Expand Down
2 changes: 1 addition & 1 deletion rust/altrios-core/src/track/path_track/path_tpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ fn extract_speed_set<'a>(
speed_sets
.iter()
.find(|&sps| sps.0 == &train_params.train_type)
.ok_or_else(|| {
.with_context(|| {
anyhow!(
"`speed_set` is `None` and `train_params.train_type` {:?} not found in `speed_sets.keys()` {:?}",
train_params.train_type,
Expand Down
13 changes: 13 additions & 0 deletions rust/altrios-core/src/train/speed_limit_train_sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ impl SpeedLimitTrainSim {
self.train_res
.update_res(&mut self.state, &self.path_tpc, &Dir::Fwd)?;
self.solve_required_pwr()?;
log::debug!(
"{}\ntime step: {}",
format_dbg!(),
self.state.time.get::<si::second>().format_eng(Some(9))
);
self.loco_con.solve_energy_consumption(
self.state.pwr_whl_out,
self.state.dt,
Expand All @@ -318,6 +323,14 @@ impl SpeedLimitTrainSim {
|| (self.state.offset < self.path_tpc.offset_end()
&& self.state.speed != si::Velocity::ZERO)
{
log::debug!(
"{}",
format_dbg!(
self.state.offset < self.path_tpc.offset_end() - 1000.0 * uc::FT
|| (self.state.offset < self.path_tpc.offset_end()
&& self.state.speed != si::Velocity::ZERO)
)
);
self.step()?;
}
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions rust/altrios-core/src/train/train_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ impl TrainSimBuilder {
// `self.origin_id` verified to be `Some` earlier
location_map
.get(self.origin_id.as_ref().unwrap())
.ok_or_else(|| {
.with_context(|| {
anyhow!(format!(
"{}\n`origin_id`: \"{}\" not found in `location_map` keys: {:?}",
format_dbg!(),
Expand All @@ -437,7 +437,7 @@ impl TrainSimBuilder {
// `self.destination_id` verified to be `Some` earlier
location_map
.get(self.destination_id.as_ref().unwrap())
.ok_or_else(|| {
.with_context(|| {
anyhow!(format!(
"{}\n`destination_id`: \"{}\" not found in `location_map` keys: {:?}",
format_dbg!(),
Expand Down

0 comments on commit 77cf2a8

Please sign in to comment.