Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b8c0300
doc(README): update lfortran version to "latest"
rouson May 29, 2026
6be9f55
feat(vector_2D_t): add operator(.dot.)
rouson May 31, 2026
eb208bd
chore(.gitignore): ignore *.csv files
rouson Jun 2, 2026
8e01c8e
chore(.gitignore): image/visualization formats
rouson Jun 3, 2026
c5a6325
feat(example): 2D advection diffusion
rouson Jun 3, 2026
1d1dcd0
test(vector_2D): dot product
rouson Jun 4, 2026
93d9df0
chore(*.dat): rm & add to .gitignore
rouson Jun 4, 2026
801ded9
WIP: encapsulate planar points and scalar products
rouson Jun 19, 2026
66dec14
fix(tensor_rank): make the result an integer
rouson Jun 21, 2026
4ef6541
fix(gradient_2D,tensor_2D): assertions
rouson Jun 21, 2026
093ccc8
build(fpm): make Julienne a dependency
rouson Jun 21, 2026
ab0b8d2
refact: mv divergence procedures to scalar-product
rouson Jun 21, 2026
c36f25f
chore(vector_2D_test): rm file_t construction
rouson Jun 22, 2026
034d74e
fix(scalar_2D_test): rm invocation of deleted proc
rouson Jun 22, 2026
6ef30fc
fix(tensor_2D_consistent): allow rank 0 (scalar)
rouson Jun 22, 2026
d5770a9
fix(2D gradients): mk components/coordinates coherent
rouson Jun 22, 2026
fc5cf2a
fix(scalar_2D_t): swap coords in grid definition
rouson Jun 23, 2026
d54a44b
doc(vector-gnuplot): fix file name in usage info
rouson Jun 23, 2026
4712050
feat(2D-stagnation-point): replace vortex example
rouson Jun 24, 2026
fdd93c0
doc(scalar-gnuplot): fix flag in usage info
rouson Jun 24, 2026
8c2b1d1
feat(2D-vector-field.gnuplot): normalize vectors
rouson Jun 24, 2026
f7f3f5f
fix(vector_2D): at_cell_centers -> co_located comp
rouson Jul 4, 2026
632d18d
fix(gnuplot): read header & mk labels correctly
rouson Jul 4, 2026
61009ce
chore(2D-stagnation): simpler filename definition
rouson Jul 4, 2026
efe8d7a
refac(vector_2D to_file): rm blank lines
rouson Jul 4, 2026
8130832
Revert "fix(gnuplot): read header & mk labels correctly"
rouson Jul 4, 2026
734abe5
fix(gnuplot): 1 name in title for vectors
rouson Jul 4, 2026
afd87e7
chore(2D-stagnation): delete unused variable
rouson Jul 4, 2026
7051f2b
build(fpm): use Julienne 4.1.0 for stop_and_print
rouson Jul 4, 2026
3766898
fix(vector_2D_divergence): clarify with temp vars
rouson Jul 5, 2026
93f6530
test(vector_2D dot): add 4th-order checks
rouson Jul 5, 2026
43da8c7
refac(vector_2D): scalar_2D_t dot product result
rouson Jul 5, 2026
86b31d3
refac(divergence_2D_t): rename scalar_product_2D_t
rouson Jul 5, 2026
59027f3
feat(2D-adv-diff): support ops for d_dt calc
rouson Jul 5, 2026
14b3bae
feat(2D-adv-diff): use stagnation-point velocity
rouson Jul 5, 2026
5c56a90
doc(scalar_2D_s): rm outdated comment in to_file
rouson Jul 5, 2026
9c72acc
feat(2D-adv-diff): ops for RK time advancement
rouson Jul 6, 2026
b19b34e
chore(gradient_2D_s): rm redundant use statement
rouson Jul 6, 2026
a2e9429
chore(vector_2D_s): rm redundant use association
rouson Jul 6, 2026
f66342e
chore: rm all 3D files
rouson Jul 6, 2026
09cf1e5
fix(2D-adv-diff): rm legacy extension
rouson Jul 6, 2026
fe5d650
chore(burgers-1D): rm unused const & var
rouson Jul 6, 2026
4d7b424
test(grad op 1D): 1.1x tolerance for gfortran
rouson Jul 6, 2026
df3c9cd
build(div-grad-lap): work around gfortran bug
rouson Jul 6, 2026
f32e10f
doc(README): add adv/diff eq to Examples section
rouson Jul 6, 2026
1a1995e
doc(README): fix typo
rouson Jul 6, 2026
5931f59
Merge branch 'main' into 2D-advection-diffusion
rouson Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Imagery
*.gif
*.mp4
*.png
*.pdf

# Data files
*.csv
*.dat

# Scratch directory
scratch

Expand Down
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,26 @@ where the small residual of approximately $-.222 \times 10^{-15}$ evidences a hi
**Future work:** Formal lays a foundation for defining DSL embedded in Fortran via template requirements,
a feature of the forthcoming Fortran 2028 standard.


Examples
--------
### Highlights
As demonstrated in this repository's two-dimensional (2D) advection/diffusion partial differential equation
(PDE) solver, Formal now supports two-dimensional (2D) operators that compute the gradient (`.grad.`) of a
scalar field, the divergence (`.div.`) of vector field, and arithmetic operators sufficient to express the
advection/diffusion PDE,

$$ \partial s / \partial t = \nabla \cdot (D \nabla s) - \vec{v} \cdot \nabla s$$

in code as
```
ds_dt = .div. (D * .grad. s) - (v .dot. .grad. s)
```
where `s` is the concentration of a passive scalar quantity, `D` is a diffusion coefficient, and
`v` is a prescribed velocity field. The [2D-advection-diffusion.F90](./example/2D-advection-diffusion.F90)
program to advance the above equation over time using a Runge-Kutta scheme.

### Other example programs
See this repository's [example](./example) subdirectory for demonstrations of how
to use Formal. For usage information for each example, execute something like
```bash
Expand All @@ -71,11 +89,11 @@ compiling [fpm-0.12.0.F90] and placing the resulting executable file in your

Building and testing
--------------------
Vendor | Compiler | Version(s) Tested | Build/Test Command
----------|-------------|-----------------------|-------------------
LFortran | `lfortran` | latest | `fpm test --compiler lfortran --flag "--cpp --realloc-lhs-arrays"`
LLVM | `flang` | 20-22 | `fpm test --compiler flang --profile release`
LLVM | `flang` | 19 | `fpm test --compiler flang --profile release --flag "-mmlir -allow-assumed-rank"`
Vendor | Compiler | Version(s) Tested | Build/Test Command
----------|-------------|-------------------|-------------------
LFortran | `lfortran` | latest | `fpm test --compiler lfortran --flag "--cpp --realloc-lhs-arrays"`
LLVM | `flang` | 20-22 | `fpm test --compiler flang --profile release`
LLVM | `flang` | 19 | `fpm test --compiler flang --profile release --flag "-mmlir -allow-assumed-rank"`

### `fpm` versions before 0.13.0
With LLVM, replace the `flang` with `flang-new` and delete `--profile release` from the above commands.
Expand Down
79 changes: 79 additions & 0 deletions example/2D-advection-diffusion.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
! Copyright (c) 2026, The Regents of the University of California
! Terms of use are as specified in LICENSE.txt

module fields_m
implicit none

integer, parameter :: space_dimension = 2

contains

pure function scalar_field(x,y) result(gaussian)
double precision, intent(in) :: x(:), y(:)
double precision gaussian(size(x),size(y))
double precision, parameter :: pi = acos(-1D0)
double precision, parameter :: x0 = 1D0, y0 = 1D0, sigma = pi/5
do concurrent(integer :: j=1:size(y)) default(none) shared(x,y,gaussian)
associate(r => sqrt((x-x0)**2 + (y(j)-y0)**2))
gaussian(:,j) = exp(-(r**2)/(2*sigma**2))
end associate
end do
end function

pure function stagnation_point_velocity(x,y) result(grad_phi)
double precision, intent(in) :: x(:), y(:)
double precision grad_phi(size(x),size(y),space_dimension)
do concurrent(integer :: i=1:size(x), j=1:size(y))
grad_phi(i,j,:) = [x(i), -y(j)]
end do
end function

end module

program advection_diffusion_2D
!! Solve the advection-diffusion equation for a passive scalar moving through a static
!! 2D velocity field.
use julienne_m, only : file_t
use fields_m, only : scalar_field, stagnation_point_velocity
use formal_m, only : scalar_2D_t, vector_2D_t, scalar_2D_initializer_i, vector_2D_initializer_i
implicit none

integer, parameter :: order = 4
procedure(scalar_2D_initializer_i), pointer :: scalar_2D_initializer
procedure(vector_2D_initializer_i), pointer :: velocity_2D_initializer
type(scalar_2D_t) s

scalar_2D_initializer => scalar_field
velocity_2D_initializer => stagnation_point_velocity

s = scalar_2D_t(scalar_2D_initializer, order=4, cells=[20,20], x_min=[-2D0,-2D0], x_max=[2D0,2D0])

associate(v => vector_2D_t(velocity_2D_initializer, mold=s))

advance_time: &
block
double precision :: dt = 1D-6
s = s + dt * d_dt(s, v)
end block advance_time

associate( &
scalar_file => s%to_file("scalar") &
,velocity_file => v%to_file("vector") &
)
call scalar_file%write_lines("example/scripts/scalar.csv")
call velocity_file%write_lines("example/scripts/velocity.csv")
end associate

end associate

contains

pure function d_dt(s, v) result(ds_dt)
type(scalar_2D_t), intent(in) :: s
type(vector_2D_t), intent(in) :: v
type(scalar_2D_t) ds_dt
double precision, parameter :: D = 1D0
ds_dt = .div. (D * .grad. s) - (v .dot. .grad. s)
end function

end program
2 changes: 1 addition & 1 deletion example/2D-sink.F90
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ program sink_2D

associate(v => vector_2D_t(vector_2D_initializer, order=order, cells=[11,11], x_min=[-1D0,-1D0], x_max=[1D0,1D0]))
associate(div_v => .div. v, expected_divergence => divergence_2D_t(divergence_2D_initializer, mold=v))
associate(v_file => v%to_file(),div_v_file => div_v%to_file(), expected_divergence_file => expected_divergence%to_file())
associate(v_file => v%to_file("v"),div_v_file => div_v%to_file(".div. v"), expected_divergence_file => expected_divergence%to_file("expected .div. v"))
call v_file%write_lines("example/scripts/sink-velocity.csv")
call div_v_file%write_lines("example/scripts/sink-divergence.csv")
call expected_divergence_file%write_lines("example/scripts/expected-divergence.csv")
Expand Down
69 changes: 69 additions & 0 deletions example/2D-stagnation-point.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
! Copyright (c) 2026, The Regents of the University of California
! Terms of use are as specified in LICENSE.txt

module velocity_potential_m
implicit none

integer, parameter :: space_dimension = 2

contains

pure function potential(x,y) result(phi)
double precision, intent(in) :: x(:), y(:)
double precision phi(size(x),size(y))
do concurrent(integer :: j=1:size(y)) default(none) shared(x,y,phi)
phi(:,j) = (x**2 - y(j)**2)/2
end do
end function

pure function potential_gradient(x,y) result(grad_phi)
double precision, intent(in) :: x(:), y(:)
double precision grad_phi(size(x),size(y),space_dimension)
do concurrent(integer :: i=1:size(x), j=1:size(y))
grad_phi(i,j,:) = [x(i), -y(j)]
end do
end function

end module

program stagnation_point_2D
use julienne_m, only : file_t
use velocity_potential_m, only : potential, potential_gradient
use formal_m, only : scalar_2D_t, vector_2D_t, scalar_2D_initializer_i, vector_2D_initializer_i
implicit none

procedure(scalar_2D_initializer_i), pointer :: scalar_2D_initializer
procedure(vector_2D_initializer_i), pointer :: vector_2D_initializer

scalar_2D_initializer => potential
vector_2D_initializer => potential_gradient

associate( &
phi => scalar_2D_t(scalar_2D_initializer, order=4, cells=[20,20], x_min=[-2D0,-2D0], x_max=[2D0,2D0]) &
)
associate( &
v => .grad. phi &
,v_exp => vector_2D_t(vector_2D_initializer, mold=phi) &
)
associate( &
velocity_potential_file => phi%to_file("phi") &
,velocity_file => v%to_file("velocity") &
,velocity_expected_file => v_exp%to_file("expected velocity") &
)
block
character(len=*), parameter :: path = "example/scripts/"
call velocity_potential_file%write_lines(path // "velocity-potential.csv")
call velocity_file%write_lines(path // "velocity.csv")
call velocity_expected_file%write_lines(path // "expected-velocity.csv")
print *
print '(a)', "With gnuplot installed, plot the results by setting your present working"
print '(a)', "directory to formal/example/scripts and executing the following commands:" // new_line('')
print '(a)', 'gnuplot -e "base_name=' // "'velocity'" // '" 2D-vector-field.gnuplot'
print '(a)', 'gnuplot -e "base_name=' // "'expected-velocity'" // '" 2D-vector-field.gnuplot'
print '(a)', 'gnuplot -e "base_name=' // "'velocity-potential'" // '" 2D-scalar-field.gnuplot' // new_line('')
end block
end associate
end associate
end associate

end program
58 changes: 0 additions & 58 deletions example/2D-vortex.F90

This file was deleted.

6 changes: 3 additions & 3 deletions example/burgers-1D.F90
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ program burgers_1D
!! * Corbino & Castillo (2020) https://doi.org/10.1016/j.cam.2019.06.042.
!! * Dumett & Castillo (2022) https://doi.org/10.13140/RG.2.2.26630.14400
use initial_condition_m, only : initial_condition
use julienne_m, only : command_line_t, csv, call_julienne_assert_, operator(.equalsExpected.), string_t
use julienne_m, only : command_line_t, call_julienne_assert_, operator(.equalsExpected.), string_t
use formal_m, only : scalar_1D_t, scalar_1D_initializer_i, d_dx, d2_dx2
use iso_fortran_env, only : output_unit
implicit none
Expand Down Expand Up @@ -67,7 +67,7 @@ program burgers_1D
block
procedure(scalar_1D_initializer_i), pointer :: scalar_1D_initializer
double precision, parameter :: pi = acos(-1D0), nu=1D0, t_final=0.6D0
double precision, allocatable :: u_surface(:,:), time(:)
double precision, allocatable :: u_surface(:,:)
double precision dt
type(scalar_1D_t) u
integer step, n
Expand Down Expand Up @@ -122,7 +122,7 @@ program burgers_1D

associate(x => u%grid())
do n = 1, size(x)
write(file_unit,"(*(G13.6,:,' '))"), x(n), u_surface(n,:) ! write space-separated values
write(file_unit,"(*(G13.6,:,' '))") x(n), u_surface(n,:) ! write space-separated values
end do
end associate

Expand Down
15 changes: 15 additions & 0 deletions example/div-grad-laplacian-1D.F90
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ subroutine output(order)
associate( s_grid => s%grid() &
,grad_s_grid => grad_s%grid() &
,laplacian_s_grid => laplacian_s%grid())
#ifndef __GFORTRAN__
associate( s_table => tabulate( &
string_t([character(len=22)::"x", "f(x) expected" , "f(x) actual" ]) &
,s_grid, f(s_grid), s%values() &
Expand All @@ -115,6 +116,20 @@ subroutine output(order)
call grad_s_table%write_lines()
call laplacian_s_table%write_lines()
end associate
#else
block
type(file_t) s_table, grad_s_table, laplacian_s_table
s_table = tabulate( string_t([character(len=22)::"x", "f(x) expected" , "f(x) actual" ]) &
,s_grid, f(s_grid), s%values() )
grad_s_table = tabulate( string_t([character(len=22)::"x", ".grad. f expected" , ".grad. f actual" ]) &
,grad_s_grid, df_dx(grad_s_grid), grad_s%values() )
laplacian_s_table = tabulate( string_t([character(len=22)::"x", ".laplacian. f expected", ".laplacian. f actual"]) &
,laplacian_s_grid, d2f_dx2(laplacian_s_grid), laplacian_s%values() )
call s_table%write_lines()
call grad_s_table%write_lines()
call laplacian_s_table%write_lines()
end block
#endif
end associate
end associate
end associate
Expand Down
2 changes: 1 addition & 1 deletion example/scripts/2D-scalar-field.gnuplot
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# 2D-scalar-field.gnuplot -- surface plot CSV
# Line 1: column labels
# Lines 2+: x, y, z data with blank lines between x-slices
# Usage: gnuplot -d "base_name='velocity-potential'" 2D-scalar-field.gnuplot
# Usage: gnuplot -e "base_name='velocity-potential'" 2D-scalar-field.gnuplot
# Default: base_name='velocity-potential'
# ============================================================================

Expand Down
Loading
Loading