Case Study 40.2 — Building the Portfolio and the Application

"You don't get a job because you can do the work. You get it because someone can see that you can do the work." — the whole argument of this case study, compressed.

Executive Summary

The previous case study read someone else's code and planned a contribution. This one builds: you will assemble the complete package that turns forty chapters of learning and one finished solver into a job application a hiring manager can act on. That package has five parts — a repository shaped like real software, a README and abstract that explain it, a figure that shows it, résumé lines that frame it as high-performance computing, and an application paragraph that maps it onto a specific role. We build each in turn, produce a small compilable tool that generates the project's metadata card, and finish by looking at the application through the eyes of the person who will read it. This is one tier harder than Case Study 40.1 because the deliverable is not a single change but a coherent, self-consistent presentation of you.

Skills applied - Packaging a real code: source tree, README, license — §36.1, §36.2 (Chapter 36) and the Project Checkpoint - Generating formatted metadata with internal-file writes — Chapter 12 - A small memory/size computation — Chapter 5, Chapter 20 (precision) - Framing Fortran as an HPC asset paired with Python/C — §40.4 - Mapping evidence to a role on the science↔software spectrum — §40.2, §40.3

Background

A portfolio is not a pile of files; it is an argument, and like any argument it must be coherent. Every piece should say the same thing about you — "I can build correct, fast, well-engineered scientific software, and I know how it connects to Python and the cluster" — and no piece should contradict the others. The most common failure is not weak work; it is invisible or inconsistent work: a strong solver with no README, a résumé that says "HPC" beside a repository with no parallel code, an abstract claiming MPI that the code does not contain. Building the package well means making one true, consistent case and making it easy to see. We build it in the order a reader encounters it.

Phase 1 — Assemble the Repository

The repository is the foundation; everything else points at it. Give it the shape a reviewer expects — the src/ + app/ + test/ layout your solver has carried since Chapter 36 — plus the two files that turn code into a project: a README and a LICENSE.

heat-solver/
├── README.md          the argument, in prose (Phase 2)
├── LICENSE            MIT — so others may actually use it
├── fpm.toml           build manifest: fpm build && fpm run
├── src/               kinds, heat_types, heat_solver, heat_io, timers
├── app/main.f90       the driver
├── test/              pFUnit + regression tests (Chapter 37)
└── figures/           steady_state.png  (Phase 3)

The LICENSE is not decoration. Code with no license is, by default, code no one is legally free to reuse — so an unlicensed "portfolio" repository quietly signals that you have not thought about how software is used. Add a specific, conventional license (MIT and BSD-3-Clause are permissive and standard for a portfolio project) as a deliberate choice. It costs one file and communicates professionalism.

Phase 2 — Generate the README's Metadata Card

The README opens with a title, an abstract, and a few hard facts — grid size, memory footprint, build command. Those facts should be computed, not guessed, and there is something fitting about generating them with the language the project is written in. Here is a small tool that prints the metadata card; it uses an internal-file write (Chapter 12) to build the abstract line and a real(dp) computation for the memory estimate.

program portfolio_report
  use, intrinsic :: iso_fortran_env, only: dp => real64, int64
  implicit none
  integer, parameter :: nx = 256, ny = 256
  integer(int64)     :: npts, bytes
  real(dp)           :: mib
  character(len=120) :: absline

  npts  = int(nx, int64) * int(ny, int64)          ! 65536 grid points
  bytes = npts * 8_int64                            ! 8 bytes per real64 -> 524288
  mib   = real(bytes, dp) / 1048576.0_dp            ! -> 0.5 MiB per field

  write(absline, '(a,i0,a,i0,a)')                                  &
        'Parallel 2D heat solver on a ', nx, 'x', ny,              &
        ' grid (modern Fortran, OpenMP+MPI).'

  print '(a)', '# Parallel 2D Heat-Equation Solver'
  print '(a)', ''
  print '(a)', trim(absline)
  print '(a, i0)',             'grid points : ', npts
  print '(a, i0, a, f0.2, a)', 'field memory: ', bytes, ' bytes (', mib, ' MiB per field)'
  print '(a)', 'build       : fpm build && fpm run'
end program portfolio_report
$ gfortran -std=f2018 -Wall -O2 portfolio_report.f90 -o report && ./report
# Parallel 2D Heat-Equation Solver

Parallel 2D heat solver on a 256x256 grid (modern Fortran, OpenMP+MPI).
grid points : 65536
field memory: 524288 bytes (0.50 MiB per field)
build       : fpm build && fpm run

Hand computation (sanity check). $256 \times 256 = 65536$ grid points; at $8$ bytes per double, one field is $65536 \times 8 = 524288$ bytes, and $524288 / 1048576 = 0.5$ MiB exactly (since $1048576 = 2 \times 524288$). A working solver holds at least two such fields at once (the current temperature and the Laplacian), so budget roughly $1$ MiB here — trivial at this size, but the same arithmetic tells you a $10000 \times 10000$ run needs about $800$ MiB per field, which is exactly the back-of-the-envelope estimate (Chapter 20) you would do before requesting nodes. The metadata card doubles as a capacity check.

Phase 3 — The Figure

Prose tells; a picture shows. Your solver already writes VTK per timestep (Chapter 26); open the final state in ParaView and export one clean image into figures/.

Described Figure — steady_state.png. A square domain. The top edge burns at the fixed hot temperature; the other three edges are held cold. Inside, color grades smoothly from hot at the top through yellows and greens to deep blue in the cold lower corners — the visible signature of heat diffusing inward to a steady state. A labeled colorbar gives the scale, and the caption states the grid size, the boundary conditions, and that the result matches the analytical steady solution. One image makes the reader believe the code is real before they read a word of it.

Phase 4 — The Résumé Lines

Now frame it. The rule from §40.4: present Fortran as modern high-performance computing, pair it with Python and the HPC toolchain, and keep every claim true. Two blocks do the work — a skills line and a project line.

Skills:
  Modern Fortran (2018) · Python (NumPy, matplotlib) · C interop (iso_c_binding)
  HPC: OpenMP, MPI, coarrays · profiling (gprof) · cache & vectorization tuning
  Numerical methods: finite differences, LAPACK/BLAS linear algebra, ODE/PDE solvers
  Software engineering: git · unit + regression testing (pFUnit) · CI · reproducible builds

Project — Parallel 2D Heat-Equation Solver (link):
  Modular finite-difference PDE solver in modern Fortran; OpenMP + MPI parallelism;
  f2py Python driver; VTK output for ParaView; validated against the analytical
  steady state; built and tested with fpm under continuous integration.

Notice the discipline: the project line names methods (finite differences), libraries (LAPACK, f2py, fpm), and engineering (validation, CI) — the specifics a specialist recognizes — and it does not claim a benchmark you did not run. If you have measured a speedup on real hardware, add it and label it as a measurement ("≈8× on 8 OpenMP threads, one 8-core node"), never as a universal promise. Every clause is something the code in Phase 1 actually does.

The consistency test. Read the résumé lines and the repository side by side. Does the code contain the MPI the résumé claims? Do the tests exist? Is there really a Python driver? If any answer is no, fix the code or the claim until they agree. An inconsistency here is the fastest way to lose a reader's trust, and the discipline of matching claim to reality is the same one this whole book modeled about benchmark numbers.

Phase 5 — The Application, Through the Reader's Eyes

The last piece maps your evidence onto a specific role. Recall the spectrum from §40.2: domain scientist → computational scientist → HPC engineer → research software engineer. Read a real posting (from a lab's careers site, a weather center like ECMWF or the Met Office, or a university RSE group) and locate it on that spectrum, then lead with the matching evidence:

If the role leans… Lead with From
Science (computational scientist) the numerical methods, validation, the physics you understand Parts V, IX
Software (RSE) the architecture, tests, CI, reproducibility, the clean modules Chapters 36, 37
Performance (HPC engineer) profiling, the optimized stencil, OpenMP/MPI scaling Parts VII, VIII

A cover paragraph then makes the case in the reader's language. A template to adapt — not to copy, and never around a fabricated posting:

I build fast, well-tested scientific software in modern Fortran. My portfolio project is a modular, parallel 2-D heat-equation solver: a five-point finite-difference core, OpenMP and MPI parallelism, a Python driver via f2py, VTK visualization, and a regression test suite run under CI — validated against the analytical steady-state solution. I am comfortable across the stack this role needs: the numerical method, the performance work to make it scale, and the software engineering to keep it correct and reproducible. I would bring that same discipline to [the model / the code / the team named in the posting].

Every sentence is backed by a file in the repository. That is the whole trick: the application does not assert competence, it points at it — and because you built the thing, every pointer resolves.

Discussion Questions

  1. The case study insists every part of the portfolio "say the same thing." Give a concrete example of two parts contradicting each other, and explain why an inconsistency costs more trust than a modest but consistent claim.
  2. Why compute the memory footprint with a program rather than just stating a number in the README? What does the same arithmetic tell you before you request nodes for a large run (Chapter 20)?
  3. The résumé "project line" names specific libraries and methods rather than saying "did scientific computing." Why is the specific version more persuasive to a specialist reader — and riskier if any of it is untrue?
  4. Take a role at the software end of the spectrum (RSE) and one at the science end (computational scientist). For each, which two files in the repository would you point to first, and why?

Your Turn: Extensions

  • Option A (do it). Actually assemble your heat-solver/ repository with a real README (use portfolio_report to generate the metadata card), a LICENSE, one exported figure, and the abstract. Push it. You now have a portfolio link.
  • Option B (measure honestly). If you can run the parallel solver, measure a real speedup on your machine, record the exact hardware and flags (Chapter 30), and add one labeled, reproducible number to the project line. Then delete any claim you could not measure.
  • Option C (target it). Find one real posting, place it on the science↔software spectrum, and rewrite the cover paragraph to lead with the matching evidence. Note which two repository files you would link first.

Key Takeaways

  • A portfolio is a coherent argument, not a pile of files: repository, README/abstract, figure, résumé lines, and application must all say the same true thing about you.
  • Generate hard facts (grid size, memory) rather than guessing them — the same arithmetic is your pre-run capacity check on a real cluster.
  • Frame Fortran as modern HPC, pair it with Python and the toolchain, name specific methods and libraries, and keep every claim consistent with what the code actually does.
  • The application does not assert competence; it points at evidence. Because you built the solver across forty chapters, every pointer resolves — which is exactly the advantage this book set out to give you.