Absolute vs Relative Path#
by Silvia Mazzoni, DesignSafe, 2025
When you tell a program where a file lives, you give it a path. On Unix-like systems (Linux, macOS)—including DesignSafe HPC clusters—paths are interpreted relative to either the filesystem root or your current working directory. Picking the right kind isn’t just pedantry: it affects permissions, reproducibility, and whether your jobs can find inputs or write outputs.
Relative vs. Full (Absolute) Paths#
Relative path
Interpreted from your current working directory (CWD).
Short and convenient when you’re “already in” the right folder.
Does not start with
/.Changes meaning if your CWD changes.
Full (absolute) path
The complete address from the filesystem root, starting with /.
Works regardless of CWD—safer for batch jobs, Tapis jobs, and SLURM.
Often uses env vars ($HOME, $WORK, $SCRATCH) for portability.
What Jupyter shows vs. what the OS/HPC uses
The Jupyter file browser and notebooks operate relative to the notebook’s CWD, so data/run1.csv “just works” there.
HPC/Tapis launchers may change CWD; absolute paths are more reliable in wrappers and batch jobs.
Quick examples#
Context |
You write |
Resolves to |
|---|---|---|
Relative (CWD = /home/user/book/) |
data/run1.csv |
/home/user/book/data/run1.csv |
“Go up one level” |
../inputs/record.at2 |
/home/user/inputs/record.at2 |
Full (absolute) |
/home/user/book/data/run1.csv |
Always that path |
Home shortcut |
~/book/data/run1.csv |
$HOME/book/data/run1.csv |
HPC env var |
$WORK/myproj/cases/c1 |
Your site’s “work” area |
Python (portable) |
Path(“data/run1.csv”).resolve() |
Absolute path computed from CWD |
When to Use Relative vs. Full Paths#
Use relative paths for quick local work inside Jupyter when you control CWD (notebooks, small scripts).
Use full paths when:
Submitting Tapis or SLURM jobs (launchers may change CWD).
Sharing inputs/outputs across scripts, nodes, or users.
Writing reusable automation that must run from any directory.
Practical Recipes#
In a Notebook (Python):
from pathlib import Path
print("CWD:", Path.cwd()) # where you are
print("Absolute:", Path("data/run1.csv").resolve()) # get a full path
print("Home:", Path("~").expanduser()) # your home dir
In a shell (terminal or notebook cell):
pwd # show current directory
echo "$HOME" # home
echo "$WORK" # HPC work area (if defined by site)
# show absolute path (many Linux systems)
readlink -f data/run1.csv
Anchor relative paths to a script’s location (Python):
from pathlib import Path
BASE = Path(__file__).resolve().parent
inp = BASE / "inputs" / "model.tcl" # stable even if CWD changes
Keep outputs portable (Python):
outdir = Path("~/results").expanduser()
outdir.mkdir(parents=True, exist_ok=True)
f = outdir / "run1.csv"
Tapis transfers (conceptual):
Think:
(systemId="designsafe.storage.default", path="/Users/you/inputs/model.tcl")For HPC job I/O, your wrapper will typically use absolute filesystem paths on the compute system (e.g.,
$WORK/myproj/...).
The mental model#
Filesystem root:
/Every absolute location starts here.Current working directory (CWD): what
pwdprints. Relative paths are resolved starting from here.Special shorthands:
.= “this directory”..= “one directory up”~or$HOME= your home directory (expanded by the shell)Environment vars like
$WORK,$SCRATCH(HPC-specific) expand to absolute paths.
Symlinks: a path might point to another path under the hood. Use
readlink -f <path>to see the real, canonical path.
Definitions#
Absolute path: starts with
/and spells out the location from the root of the filesystem.Example:
/home/silvia/projects/opensees/input.tcl
Relative path: does not start with
/; it’s interpreted relative to the CWD.Example:
../input.tcl,./scripts/run.sh,results/output/
Why this is extra important on DesignSafe/HPC#
Permissions & safety: Absolute paths can jump outside your home or project area. If you write to something like
/data/designsafe/mydata/…without your username in the path, you’ll hit “permission denied.” Relative paths (from your CWD) and$HOME/$WORKvariables keep you inside places you own.Portability: Relative paths (or
$HOME/$WORK) make scripts runnable across clusters/accounts without hardcoding user-specific directories.Batch jobs: Schedulers launch jobs in specific working directories. A relative
./input/will work if the job’s CWD is set correctly; an absolute/…will ignore the CWD entirely.
Examples#
# Absolute paths (start at root '/')
/home/silvia/projects/sim/input.tcl
/work/01234/silvia/opensees/examples/model.tcl
# Relative paths (resolved from your current directory)
../input.tcl
./scripts/run.sh
results/output/
# Shorthand expands to an absolute path
$HOME/projects/sim/input.tcl
$WORK/opensees/examples/model.tcl
~/projects/sim/input.tcl
⚠️ On Windows, absolute paths start with a drive letter like
C:\…. The/rule is Unix-specific—but that’s exactly what you’ll use on DesignSafe.
Tapis & DesignSafe specifics you’ll actually use#
Tapis Files URIs use
tapis://<systemId>/<path>(not your shell’s CWD). On DesignSafe storage, your home istapis://designsafe.storage.default/<username>/…. In the CLI, a path likedesignsafe.storage.default:apps/my-app/1.0/is relative to your storage home; adding a leading slash may target the system’s root and cause permission errors.Good patterns:
Use relative paths in your repo/scripts and rely on
cwdbeing set in your job.Use
$HOME/$WORKfor absolute paths in HPC scripts (portable and readable).In Tapis
fileInputs, prefertapis://designsafe.storage.default/<username>/…or just keep paths relative to the system’s configured root/home, as your platform expects.
Common pitfalls (and quick fixes)#
“Permission denied” when creating files You probably used an absolute path outside your writable area. Switch to a relative path under your home, or use
$HOME/$WORK.Spaces in filenames Quote your paths:
"/home/silvia/My Project/input.tcl"or escape the spaces.Mixed slashes Use forward slashes
/everywhere on Unix/HPC. Backslashes are not path separators here.Assuming
~works in every tool Shell expands~, but some tools bypass the shell; prefer$HOME/…or an absolute path.
Quick commands to sanity-check#
pwd # your current working directory
ls -l <path> # does it exist and do you have rights?
readlink -f <path> # canonical, absolute path (resolves symlinks)
echo "$HOME" "$WORK" # expand HPC environment variables
If you keep these rules in mind—absolute paths for clarity when you must, relative (or $HOME/$WORK) for portability—you’ll avoid the most common HPC path headaches.
expanduser and abspath#
Getting the full absolute path
You can define the full path with or without ~ (a special UNIX shorthand for your home directory).
Let’s make sure you can build robust absolute paths, no matter how the path is specified.
Why this matters:#
Paths without ~ are interpreted relative to your current working directory.
Different environments on DesignSafe mount your storage under different root directories. If you rely on relative paths without care, your code might break or point to unexpected locations.
Using expanduser and abspath together#
os.path.expanduser(path) replaces ~ with your actual home directory.
os.path.abspath(path) resolves any relative path (like MyData) to a full path from /, based on where you’re currently working.
But each does only half the job:
import os
expanduser#
os.path.expanduser('~/MyData') # replaces ~ with your actual home directory
'/home/jupyter/MyData'
os.path.exists(os.path.expanduser('~/MyData'))
True
os.path.expanduser('/MyData')
'/MyData'
os.path.exists(os.path.expanduser('/MyData'))
False
os.path.expanduser('MyData') # (unchanged, still relative)
'MyData'
os.path.exists(os.path.expanduser('MyData'))
False
abspath#
os.path.abspath('MyData') # (convert relative to absolute, based on cwd)
'/home/jupyter/MyData/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Jupyter_Notebooks/MyData'
os.path.exists(os.path.abspath('MyData'))
False
os.path.abspath('~/MyData') ## gives unrealistic results
'/home/jupyter/MyData/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Jupyter_Notebooks/~/MyData'
os.path.exists(os.path.abspath('~/MyData'))
False
Combine abspath expanduser#
The safest pattern is to combine them. This way, your path works whether it starts with ~ or is just relative:
os.path.abspath(os.path.expanduser('~/MyData'))
'/home/jupyter/MyData'
os.path.exists(os.path.abspath(os.path.expanduser('~/MyData')))
True
os.path.abspath(os.path.expanduser('MyData')) # this is a relative path, so it works only when MyData exists
'/home/jupyter/MyData/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Jupyter_Notebooks/MyData'
os.path.exists('MyData')
False
os.path.exists(os.path.abspath(os.path.expanduser('MyData')))
False
Rule of thumb#
Always use
expanduserto safely expand~.Always follow it with
abspathto ensure you get a full path from/.
This tiny two-step makes your code portable and predictable—especially across JupyterHub, the OpenSees VM, Stampede3, and Tapis, where your storage may appear under different absolute root directories.
Understanding how to combine expanduser and abspath ensures your scripts will find your data reliably, saving you from countless FileNotFoundError surprises—no matter which system you’re running on.
Summary of absolute and relative paths#
Always use absolute paths for reliability.
Use
os.path.expanduser("~")to safely handle home directories.Prefer
os.pathfunctions over shell commands for portability across DesignSafe systems.Remember: the same storage might appear differently in JupyterHub, the OpenSees VM, Stampede3, and Tapis — understanding paths keeps your workflows smooth and error-free.