Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
- name: Install dependencies
run: |
brew update && (brew list cmake || brew install cmake)
brew install openblas eigen libomp lapack
brew install openblas eigen@3 libomp lapack
sudo ln -sf /opt/homebrew/bin/gfortran-14 /opt/homebrew/bin/gfortran

- name: Export Environment Variables
Expand All @@ -92,13 +92,14 @@ jobs:
export CPPFLAGS+=" -I/opt/homebrew/opt/libomp/include"
export LDFLAGS+=" -L/opt/homebrew/opt/lapack/lib"
export CPPFLAGS+=" -I/opt/homebrew/opt/lapack/include"
export CPPFLAGS+=" -I/opt/homebrew/opt/eigen@3/include"
export PKG_CONFIG_PATH+=" /opt/homebrew/opt/lapack/lib/pkgconfig"

- name: Create build directory
run: mkdir -p build

- name: Run CMake
run: cmake -DCMAKE_Fortran_COMPILER="/opt/homebrew/bin/gfortran-14" -S . -B build
run: cmake -DEigen3_DIR=/opt/homebrew/Cellar/eigen@3/3.4.1/share/eigen3/cmake -DCMAKE_Fortran_COMPILER="/opt/homebrew/bin/gfortran-14" -S . -B build

- name: Build MOLE library
run: cmake --build build
Expand Down
36 changes: 20 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,27 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/opt/libomp/lib -L/opt/homebrew/opt/libomp/lib -lomp")
message(STATUS "Using AppleClang-specific flags.")
include_directories("/usr/local/opt/libomp/include" "/opt/homebrew/opt/libomp/include")
message(STATUS "Adding Eigen3 include directory for AppleClang.")
set(EIGEN3_INCLUDE_DIR "/opt/homebrew/include/eigen3")
include_directories(${EIGEN3_INCLUDE_DIR})

elseif (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
set(CMAKE_CXX_FLAGS "-O3 -qopenmp -DARMA_DONT_USE_WRAPPER -DARMA_USE_SUPERLU -diag-disable=10430")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
message(STATUS "Using non-Clang compiler flags.")
# Get MKLROOT from environment or fallback to default
if(DEFINED ENV{MKLROOT})
set(MKLROOT $ENV{MKLROOT})
else()
set(MKLROOT "/opt/intel/oneapi/mkl/latest")
endif()

# Automatically set Armadillo to link against MKL
set(BLAS_LIBRARIES "${MKLROOT}/lib/intel64/libmkl_rt.so" CACHE STRING "BLAS library path for MKL")
set(LAPACK_LIBRARIES "${MKLROOT}/lib/intel64/libmkl_rt.so" CACHE STRING "LAPACK library path for MKL")
set(ARMA_USE_WRAPPER OFF CACHE BOOL "Disable Armadillo wrapper to directly use MKL")

message(STATUS "Using MKL from: ${MKLROOT}")
set(CMAKE_CXX_FLAGS "-O3 -qopenmp -DARMA_DONT_USE_WRAPPER -DARMA_USE_SUPERLU -diag-disable=10430")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
message(STATUS "Using non-Clang compiler flags.")
# Get MKLROOT from environment or fallback to default
if(DEFINED ENV{MKLROOT})
set(MKLROOT $ENV{MKLROOT})
else()
set(MKLROOT "/opt/intel/oneapi/mkl/latest")
endif()
# Automatically set Armadillo to link against MKL
set(BLAS_LIBRARIES "${MKLROOT}/lib/intel64/libmkl_rt.so" CACHE STRING "BLAS library path for MKL")
set(LAPACK_LIBRARIES "${MKLROOT}/lib/intel64/libmkl_rt.so" CACHE STRING "LAPACK library path for MKL")
set(ARMA_USE_WRAPPER OFF CACHE BOOL "Disable Armadillo wrapper to directly use MKL")

message(STATUS "Using MKL from: ${MKLROOT}")

else()
set(CMAKE_CXX_FLAGS "-O3 -fopenmp -DARMA_DONT_USE_WRAPPER -DARMA_USE_SUPERLU")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
Expand Down
9 changes: 9 additions & 0 deletions doc/sphinx/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,24 @@ $(SPHINX_TARGETS):

.PHONY: copy-images
copy-images:
@echo "[IMAGE_COPY_DEBUG] ===== Starting image copy process ====="
@echo "[IMAGE_COPY_DEBUG] Copying images from ../assets/img/ to source/_images/"
mkdir -p source/_images
@ls -la ../assets/img/*.png 2>/dev/null | grep -E "MOLE_pillars|MOLE_OSE_circles" || echo "[IMAGE_COPY_DEBUG] Source images not found"
cp -r ../assets/img/* source/_images/ || true
@echo "[IMAGE_COPY_DEBUG] Images copied to source/_images/:"
@ls -la source/_images/*.png 2>/dev/null | grep -E "MOLE_pillars|MOLE_OSE_circles" || echo "[IMAGE_COPY_DEBUG] Images not in source/_images/"
# Copy README images to the _images directory for proper reference
@echo "[IMAGE_COPY_DEBUG] Copying images to source/intros/doc/assets/img/"
mkdir -p source/intros/doc/assets/img
cp -r ../assets/img/* source/intros/doc/assets/img/ || true
@echo "[IMAGE_COPY_DEBUG] Images copied to source/intros/doc/assets/img/:"
@ls -la source/intros/doc/assets/img/*.png 2>/dev/null | grep -E "MOLE_pillars|MOLE_OSE_circles" || echo "[IMAGE_COPY_DEBUG] Images not in source/intros/doc/assets/img/"
# Create figure directories for SVG files
mkdir -p source/api/examples/md/figures
mkdir -p source/api/examples-m/md/figures
mkdir -p source/math_functions/figures
@echo "[IMAGE_COPY_DEBUG] ===== Image copy process complete ====="

.PHONY: clean
clean:
Expand Down
147 changes: 147 additions & 0 deletions doc/sphinx/source/_ext/image_path_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
"""
Sphinx extension to log image path resolution for debugging image inclusion issues.

This extension hooks into Sphinx's document reading and image processing to log:
1. When documents with includes are processed
2. Image references found in markdown files
3. Path resolution attempts
4. Final image paths used by Sphinx
"""
import os
import logging
from pathlib import Path
from docutils import nodes
from sphinx.util import logging as sphinx_logging

logger = sphinx_logging.getLogger(__name__)

def log_document_read(app, doctree):
"""Log when a document is read and what images it contains."""
docname = app.env.docname

# Get the source file path
source_file = app.env.doc2path(docname, base=None)

# Check if this is an OSE organization wrapper document
if 'ose_organization' in docname:
logger.info("=" * 80)
logger.info(f"[IMAGE_DEBUG] Processing document: {docname}")
logger.info(f"[IMAGE_DEBUG] Source file: {source_file}")
logger.info(f"[IMAGE_DEBUG] Source directory: {os.path.dirname(source_file)}")

# Check for image nodes
for node in doctree.traverse(nodes.image):
uri = node.get('uri', 'NO_URI')
logger.info(f"[IMAGE_DEBUG] Found image node with URI: {uri}")

# Try to resolve the full path
if hasattr(app.env, 'relfn2path'):
try:
rel_fn, abs_fn = app.env.relfn2path(uri, docname)
logger.info(f"[IMAGE_DEBUG] Resolved relative path: {rel_fn}")
logger.info(f"[IMAGE_DEBUG] Resolved absolute path: {abs_fn}")
logger.info(f"[IMAGE_DEBUG] File exists: {os.path.exists(abs_fn)}")
except Exception as e:
logger.warning(f"[IMAGE_DEBUG] Failed to resolve path: {e}")

logger.info("=" * 80)


def log_missing_reference(app, env, node, contnode):
"""Log missing reference attempts (including images)."""
if isinstance(node, nodes.image):
uri = node.get('uri', 'UNKNOWN')
logger.warning(f"[IMAGE_DEBUG] Missing image reference: {uri}")
logger.warning(f"[IMAGE_DEBUG] Current docname: {env.docname}")
logger.warning(f"[IMAGE_DEBUG] Current doc path: {env.doc2path(env.docname)}")
return None


def log_image_copying(app, exception):
"""Log image copying at the end of the build."""
if exception is not None:
return

logger.info("=" * 80)
logger.info("[IMAGE_DEBUG] Build finished - checking copied images")

# Check what images ended up in _images
build_images_dir = Path(app.outdir) / "_images"
if build_images_dir.exists():
logger.info(f"[IMAGE_DEBUG] Images directory: {build_images_dir}")
logger.info(f"[IMAGE_DEBUG] Images in _images directory:")
for img in sorted(build_images_dir.glob("*.png")):
logger.info(f"[IMAGE_DEBUG] - {img.name} ({img.stat().st_size} bytes)")
else:
logger.warning(f"[IMAGE_DEBUG] Images directory does not exist: {build_images_dir}")

logger.info("=" * 80)


def trace_myst_include_processing(app, docname, source):
"""
Hook into source-read event to log MyST include processing.
This runs BEFORE MyST parses the document.
"""
if 'ose_organization' in docname:
logger.info("=" * 80)
logger.info(f"[IMAGE_DEBUG] Source-read event for: {docname}")

# Check if this file has an include directive
if '{include}' in source[0]:
logger.info(f"[IMAGE_DEBUG] Document contains include directive")

# Extract the included file path
import re
include_match = re.search(r'\{include\}\s+([^\s\n]+)', source[0])
if include_match:
included_path = include_match.group(1)
logger.info(f"[IMAGE_DEBUG] Including file: {included_path}")

# Resolve the full path
source_dir = Path(app.env.doc2path(docname, base=None)).parent
full_included_path = (source_dir / included_path).resolve()
logger.info(f"[IMAGE_DEBUG] Source document dir: {source_dir}")
logger.info(f"[IMAGE_DEBUG] Resolved include path: {full_included_path}")
logger.info(f"[IMAGE_DEBUG] Include file exists: {full_included_path.exists()}")

if full_included_path.exists():
# Read the included file and look for image references
with open(full_included_path, 'r') as f:
included_content = f.read()

# Find markdown image references
image_matches = re.findall(r'!\[([^\]]*)\]\(([^)]+)\)', included_content)
if image_matches:
logger.info(f"[IMAGE_DEBUG] Found {len(image_matches)} image(s) in included file:")
for alt_text, img_path in image_matches:
logger.info(f"[IMAGE_DEBUG] - Alt: '{alt_text}', Path: '{img_path}'")

# Check if image path is relative to included file or source file
# Path relative to included file's location
rel_to_included = (full_included_path.parent / img_path).resolve()
logger.info(f"[IMAGE_DEBUG] Relative to included file: {rel_to_included}")
logger.info(f"[IMAGE_DEBUG] Exists (rel to included): {rel_to_included.exists()}")

# Path relative to source document
rel_to_source = (source_dir / img_path).resolve()
logger.info(f"[IMAGE_DEBUG] Relative to source doc: {rel_to_source}")
logger.info(f"[IMAGE_DEBUG] Exists (rel to source): {rel_to_source.exists()}")

logger.info("=" * 80)


def setup(app):
"""Register the extension."""
# Connect to various events
app.connect('doctree-read', log_document_read)
app.connect('missing-reference', log_missing_reference)
app.connect('build-finished', log_image_copying)
app.connect('source-read', trace_myst_include_processing)

return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

48 changes: 48 additions & 0 deletions doc/sphinx/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,58 @@ def fix_math_environments(app, docname, source):

source[0] = src

def copy_governance_images(app, config):
"""
Copy governance and organization images before Sphinx build starts.

This ensures images referenced in OSE_ORGANIZATION.md are available
when Sphinx processes the document, regardless of whether the build
is run via Makefile or directly via sphinx-build (e.g., on ReadTheDocs).

The images are copied to two locations:
1. source/_images/ - Standard Sphinx image directory
2. source/intros/doc/assets/img/ - Where MyST resolves relative paths
from the including file (ose_organization_wrapper.md)

See: GitHub Issue #222
"""
import shutil
from pathlib import Path

# Determine paths relative to conf.py location
conf_dir = Path(app.confdir)

# Source: doc/assets/img/ (relative to repo root)
img_source = conf_dir.parent.parent.parent / "doc" / "assets" / "img"

# Destinations
img_dest_1 = conf_dir / "_images"
img_dest_2 = conf_dir / "intros" / "doc" / "assets" / "img"

if not img_source.exists():
print(f"Warning: Image source directory not found: {img_source}")
return

# Copy to both locations
for dest in [img_dest_1, img_dest_2]:
dest.mkdir(parents=True, exist_ok=True)

# Copy all image files
for pattern in ["*.png", "*.jpg", "*.jpeg", "*.gif", "*.svg"]:
for img in img_source.glob(pattern):
dest_file = dest / img.name
try:
shutil.copy2(img, dest_file)
except Exception as e:
print(f"Warning: Could not copy {img.name}: {e}")

def setup(app):
"""Setup function for Sphinx extension."""
app.add_js_file('mathconf.js')

# Copy governance images before build starts (Fix for Issue #222)
app.connect('config-inited', copy_governance_images)

# Add capability to replace problematic math environments
app.connect('source-read', fix_math_environments)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ $$

This example is implemented in:
- [MATLAB/ OCTAVE](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DHomogeneousDirichlet.m)
- [C++](https://github.com/csrc-sdsu/mole/blob/main/examples/cpp/elliptic1DHomogeneousDirichlet.cpp)

Additional MATLAB/ OCTAVE variants of this example with different boundary conditions:
- [Non-Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DNonHomogeneousDirichlet.m)
- [Left Dirichlet, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightNeumann.m)
- [Left Neumann, Right Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightDirichlet.m)
- [Left Dirichlet, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightRobin.m)
- [Left Neumann, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightNeumann.m)
- [Left Neumann, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightRobin.m)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ This example is implemented in:
Additional MATLAB/ OCTAVE variants of this example with different boundary conditions:
- [Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DHomogeneousDirichlet.m)
- [Non-Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DNonHomogeneousDirichlet.m)
- [Left Dirichlet, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightNeumann.m)
- [Left Neumann, Right Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightDirichlet.m)
- [Left Neumann, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightNeumann.m)
- [Left Neumann, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightRobin.m)
- [Left Robin, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftRobinRightRobin.m)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Elliptic1D Left Dirichlet and Right Neumann Boundary Conditions
### Elliptic1D Left Neumann and Right Dirichlet Boundary Conditions

Solves the 1D Poisson equation with left Dirichlet and right Neumann boundary conditions.
Solves the 1D Poisson equation with left Neumann and right Dirichlet boundary conditions.

$$
-\nabla^2 u(x) = 1
Expand Down Expand Up @@ -43,7 +43,8 @@ $$
---

This example is implemented in:
- [MATLAB/ OCTAVE](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightNeumann.m)
- [MATLAB/ OCTAVE](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightDirichlet.m)
- [C++](https://github.com/csrc-sdsu/mole/blob/main/examples/cpp/elliptic1DLeftNeumannRightDirichlet.cpp)

Additional MATLAB/ OCTAVE variants of this example with different boundary conditions:
- [Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DHomogeneousDirichlet.m)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ $$

This example is implemented in:
- [MATLAB/ OCTAVE](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightNeumann.m)
- [C++](https://github.com/csrc-sdsu/mole/blob/main/examples/cpp/elliptic1DLeftNeumannRightNeumann.cpp)

Additional MATLAB/ OCTAVE variants of this example with different boundary conditions:
- [Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DHomogeneousDirichlet.m)
- [Non-Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DNonHomogeneousDirichlet.m)
- [Left Dirichlet, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightNeumann.m)
- [Left Neumann, Right Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightDirichlet.m)
- [Left Dirichlet, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightRobin.m)
- [Left Neumann, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightRobin.m)
- [Left Robin, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftRobinRightRobin.m)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ This example is implemented in:
Additional MATLAB/ OCTAVE variants of this example with different boundary conditions:
- [Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DHomogeneousDirichlet.m)
- [Non-Homogeneous Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DNonHomogeneousDirichlet.m)
- [Left Dirichlet, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightNeumann.m)
- [Left Neumann, Right Dirichlet](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightDirichlet.m)
- [Left Dirichlet, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftDirichletRightRobin.m)
- [Left Neumann, Right Neumann](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftNeumannRightNeumann.m)
- [Left Robin, Right Robin](https://github.com/csrc-sdsu/mole/blob/main/examples/matlab_octave/elliptic1DLeftRobinRightRobin.m)
Expand Down
Loading
Loading