Try on DesignSafe

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 pwd prints. 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/$WORK variables 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 is tapis://designsafe.storage.default/<username>/…. In the CLI, a path like designsafe.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 cwd being set in your job.

    • Use $HOME / $WORK for absolute paths in HPC scripts (portable and readable).

    • In Tapis fileInputs, prefer tapis://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 expanduser to safely expand ~.

  • Always follow it with abspath to 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.path functions 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.