Tutorial 4: Gridding and phantom rotation¶
Learn computational mesh strategies and advanced near-field placement techniques.
What you'll learn¶
- Automatic vs manual gridding
- Subgridding for antenna components
- Per-frequency grid settings
- By_cheek placement with phantom rotation
- Binary search for touching angle detection
- Scene alignment optimization
Related documentation: Configuration (gridding parameters), Advanced features (phantom rotation)
Prerequisites¶
- Tutorial 3 completed (near-field basics)
- Understanding of FDTD grid concepts
- Antenna models available
Bash setup¶
Run this once per notebook session:
from pathlib import Path
import importlib.util
p = Path.cwd()
while not (p / "scripts" / "notebook_helpers.py").exists():
p = p.parent
spec = importlib.util.spec_from_file_location("_", p / "scripts" / "notebook_helpers.py")
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
run_bash = m.get_run_bash()
import IPython
IPython.core.display.max_output_size = None
This helper function lets you run bash commands from Python cells using run_bash('command').
If you're using bash directly (recommended), ignore the Python code blocks and just run the commands directly. Make sure to always run source .bashrc first.
Computational gridding basics¶
The FDTD solver discretizes space into a rectangular mesh. Cell size affects both accuracy and computation time.
Key tradeoffs:
- Smaller cells: More accurate, longer simulation time
- Larger cells: Faster simulation, potential artifacts
- Optimal size: 10-20 cells per wavelength minimum
For a 700 MHz signal: - Wavelength in tissue: ~40-60 mm (depends on tissue type) - Recommended cell size: 2-3 mm (GOLIAT enforces a 3 mm maximum for manual grids)
GOLIAT supports three gridding strategies:
| Strategy | When to use | Configuration |
|---|---|---|
| Automatic | Quick tests, uniform materials | grid_mode: "automatic" |
| Manual | Production runs, precise control | grid_mode: "manual" + per-frequency settings |
| Subgridding | Fine details (antenna parts) | Defined in antenna_config |
The GOLIAT EU Project uses manual gridding with per-frequency settings for all production simulations.
The configuration file¶
This tutorial uses the Thelonious phantom with two frequencies (700 MHz and 835 MHz) to demonstrate gridding configurations. The 700 MHz antenna uses subgridding, while 835 MHz does not.
Gridding parameters¶
The gridding_parameters section defines global mesh settings.
"gridding_parameters": {
"global_gridding": {
"grid_mode": "manual",
"manual_fallback_max_step_mm": 5.0
},
"global_gridding_per_frequency": {
"700": 2.5,
"835": 2.5
},
"padding": {
"padding_mode": "manual",
"manual_bottom_padding_mm": [0, 0, 0],
"manual_top_padding_mm": [0, 0, 0]
}
}
Key parameters:
grid_mode: Use"manual"for explicit control,"automatic"for Sim4Life defaultsmanual_fallback_max_step_mm: Maximum cell size if no per-frequency value is definedglobal_gridding_per_frequency: Cell size in mm for each frequencypadding: Extra space around simulation boundaries
The per-frequency settings let you use finer grids for higher frequencies where wavelengths are shorter.

Left: Coarse 5 mm grid. Right: Fine 2.5 mm grid. Finer grids resolve more details.
Subgridding for antennas¶
Subgridding applies a finer mesh to specific components without affecting the global grid. This is useful for small antenna features that need high resolution.
Here's the 700 MHz antenna config with subgridding:
"700": {
"center_frequency": 700,
"target_power_mW": 267,
"model_type": "PIFA",
"source_name": "Lines 1",
"materials": {...},
"gridding": {
"automatic": ["component1:Substrate", "Lines 1"],
"subgridding": {
"components": ["component1:Battery", "component1:Patch", "Extrude 1", "component1:ShortingPin"],
"SubGridMode": "Box",
"SubGridLevel": "x9",
"AutoRefinement": "AutoRefinementVeryFine"
}
}
}
Subgridding parameters:
components: List of CAD entities to apply subgrid toSubGridMode: Subgrid shape ("Box"is standard)SubGridLevel: Resolution multiplier relative to global grid"x3": 3x finer (cell size divided by 3)"x9": 9x finer (cell size divided by 9)- Higher values give better accuracy but increase computation time
AutoRefinement: Additional refinement level"AutoRefinementVeryFine": Highest quality"AutoRefinementFine": Moderate quality"AutoRefinementDefault": Standard quality
For 700 MHz with global grid 2.5 mm and SubGridLevel x9: - Global cell size: 2.5 mm - Subgrid cell size: 2.5 / 9 ≈ 0.28 mm
This resolves fine antenna features without making the entire domain expensive.

Subgrid region around antenna components (shown in red). Global grid is coarser (blue).
Comparing with and without subgridding¶
The 835 MHz antenna does not use subgridding:
"835": {
"center_frequency": 835,
"target_power_mW": 228,
"model_type": "PIFA",
"source_name": "Lines 1",
"materials": {...},
"gridding": {
"automatic": ["component1:Substrate", "Lines 1"]
}
}
This antenna uses only the global 2.5 mm grid. You can compare results to see if subgridding makes a difference for your specific antenna geometry.
By_cheek placement¶
The by_cheek placement positions the phone at the ear, using the tragus (ear cartilage protrusion) as an anatomical landmark.
Configuration¶
"placement_scenarios": {
"by_cheek": {
"bounding_box": "default",
"positions": {
"tragus": [0, 0, 0]
},
"orientations": {
"cheek_base": {
"rotate_phantom_to_cheek": true,
"angle_offset_deg": 0
},
"tilt_base": []
},
"antenna_reference": {
"distance_from_top": 10
},
"phantom_reference": "tragus"
}
}
Key differences from front_of_eyes:
- Uses
tragusinstead ofnasionas reference point - Distance from cheek is 8 mm (vs 200 mm from eyes)
- Includes base alignment calculation (ear-to-mouth vector)
- Supports phantom rotation option
Tragus landmark¶
The phantom_reference: "tragus" parameter points to coordinates defined in phantom_definitions:
"phantom_definitions": {
"thelonious": {
"tragus": [0, 7, -5],
"lips": [0, 122, 31],
...
"placements": {
"do_by_cheek": true,
"distance_from_cheek": 8
}
}
}
GOLIAT finds the ear skin entity, calculates its center, applies the tragus offset, then positions the phone 8 mm away.

Tragus anatomical landmark used as reference for by_cheek placement.
Phantom rotation¶
For some by_cheek orientations, the phantom needs to rotate toward the phone to create realistic contact. This is configured using a dictionary format in the orientations section.
Configuration¶
Parameters:
rotate_phantom_to_cheek: Enable automatic rotation (boolean)angle_offset_deg: Additional angle to rotate phantom away from phone after contact (number)
When this is enabled, GOLIAT: 1. Places phone and phantom in initial positions 2. Runs binary search to find touching angle 3. Applies the angle + offset 4. Rotates phantom, bounding boxes, and sensors 5. Continues with scene alignment
Binary search for touching angle¶
GOLIAT uses binary search to find the exact angle where the phantom's skin touches the phone's ground plane.
The search: - Searches between 0 and 30 degrees rotation around Z axis - Uses 0.5 degree precision - Checks for overlap at each iteration - Stops when touching distance is minimized
Watch the console output during setup to see the search progress:
- Handling phantom rotation...
- Finding touching angle via binary search...
- Testing angle: 15.00 deg (distance: 2.35 mm)
- Testing angle: 7.50 deg (distance: 5.82 mm)
- Testing angle: 11.25 deg (distance: 3.47 mm)
- Testing angle: 13.12 deg (distance: 2.89 mm)
...
- Determined touching angle: 13.50 deg
- Applying offset: 0.00 deg
- Final rotation: 13.50 deg
- Done in 4.8s

Console output showing binary search iterations converging to optimal angle.
Before and after rotation¶

Before rotation: Gap between phantom and phone.

After rotation: Phantom skin touches phone ground plane.
Angle offset¶
The angle_offset_deg parameter lets you fine-tune the final position. Positive values rotate the phantom away from the phone (increasing gap), negative values rotate toward it (increasing overlap).
Examples: - angle_offset_deg: 0: Phantom just touches phone (default) - angle_offset_deg: 2: 2 degree separation (small gap) - angle_offset_deg: -1: 1 degree overlap (slight compression)
Scene alignment¶
For by_cheek placements, GOLIAT automatically aligns the entire scene with the phone's upright orientation. This optimizes the computational grid and can reduce simulation time.
How it works¶
After phantom rotation (if enabled), GOLIAT:
- Identifies reference entities on phone:
- PIFA:
component1:Substrateandcomponent1:Battery - IFA:
GroundandBattery - Calculates transformation matrix to make phone upright
- Applies transformation to all scene entities:
- Phantom group
- Antenna group
- Simulation bounding box
- Antenna bounding box
- Head and trunk bounding boxes
- Point sensors
Only parent groups and bounding boxes are transformed, not individual tissues. This keeps relative geometry correct while aligning the grid with the phone.

Scene after alignment: Phone is upright, grid aligns with phone orientation.
Why this matters¶
FDTD grids are always axis-aligned rectangular meshes. When the phone is tilted relative to the axes, the grid must use smaller cells to resolve edges accurately. Aligning the phone with the axes lets the grid use larger cells while maintaining accuracy.
This can reduce: - Number of grid cells (by 10-30%) - Memory usage - Simulation time
The alignment happens automatically for all by_cheek placements. No configuration is needed.
Running the simulations¶
Run the study:
This runs 4 simulations: 1. 700 MHz, by_cheek, tragus position, cheek_base orientation (with rotation, with subgrid) 2. 700 MHz, by_cheek, tragus position, tilt_base orientation (no rotation, with subgrid) 3. 835 MHz, by_cheek, tragus position, cheek_base orientation (with rotation, no subgrid) 4. 835 MHz, by_cheek, tragus position, tilt_base orientation (no rotation, no subgrid)
Watch for these key steps in the logs:
For cheek_base orientation:
- Calculating placement details...
- Calculated base rotation for cheek alignment: 84.23 degrees around X-axis.
- Handling phantom rotation...
- Finding touching angle via binary search...
- Determined touching angle: 13.50 deg
- Aligning simulation scene with phone...
For tilt_base orientation:
- Calculating placement details...
- Calculated base rotation for cheek alignment: 84.23 degrees around X-axis.
- Handling phantom rotation...
- Phantom rotation not enabled for this orientation.
- Aligning simulation scene with phone...
The setup phase takes longer for cheek_base due to the binary search (extra 5-8 seconds).
Comparing results¶
After simulations complete, compare the four cases:
Subgridding comparison: - 700 MHz (with subgrid) vs 835 MHz (no subgrid) - Check if SAR hotspot patterns differ - Compare peak 10g SAR values
Rotation comparison: - cheek_base (with rotation) vs tilt_base (no rotation) - Check contact region SAR values - Compare field penetration depth
TODO: Analysis and visualization tools will be covered in a future tutorial.¶
Find results in:
results/near_field/thelonious/700MHz/
by_cheek_tragus_cheek_base/ # With rotation, with subgrid
by_cheek_tragus_tilt_base/ # No rotation, with subgrid
results/near_field/thelonious/835MHz/
by_cheek_tragus_cheek_base/ # With rotation, no subgrid
by_cheek_tragus_tilt_base/ # No rotation, no subgrid
Gridding recommendations¶
Based on GOLIAT EU Project experience:
For production simulations: - Use manual gridding with per-frequency settings - 2.5 mm cells for frequencies below 1000 MHz - 1.5-2.0 mm cells for frequencies 1000-3000 MHz - 1.0 mm cells for frequencies above 3000 MHz
For subgridding: - Apply to small antenna components (patches, pins, batteries) - Use SubGridLevel x9 for fine metallic features - Use AutoRefinementVeryFine for best accuracy - Skip subgridding for quick test runs
For phantom rotation: - Enable for by_cheek placements where contact matters - Use angle_offset_deg: 0 for realistic contact - Test without rotation first to verify basic setup
What's next¶
You've learned advanced near-field techniques. Next steps:
Tutorial 5: Parallel and cloud execution - Running multiple simulations simultaneously - Config splitting strategies - Using oSPARC for cloud compute
Summary¶
You learned: - FDTD grids discretize space into rectangular meshes with tradeoffs between accuracy and speed - Manual gridding with per-frequency settings provides precise control - Subgridding applies finer resolution to antenna components without affecting global grid - By_cheek placement uses tragus landmark and includes automatic base alignment - Phantom rotation uses binary search to find optimal touching angle - Scene alignment optimizes grid orientation for by_cheek placements automatically - The GOLIAT EU Project uses manual 2.5 mm grids with x9 subgridding for production
In Tutorial 5, we'll explore running multiple simulations in parallel to use multi-core systems and cloud compute resources.