Electromagnetic simulation of MRI transmit coils and virtual observation points for SAR, ported from MARIE 3.0.
pip install mariepyBuild a coil and a body, and solve them together at the Larmor frequency of the
field strength you name. Both are built in code here. A case laid out as MARIE
lays it out — a simulation file in data/inputs/, its body in data/bodies/,
its coil in data/coils/coil_files/ — is read whole:
from mariepy.inputs import read_case
case = read_case("data/inputs/my_case.json")
result = solve(
case.body, case.coil, case.medium, linear=case.linear, shield=case.shield
)VoxelBody.read_marie, SurfaceMesh.read_gmsh22 and read_lumped_elements
read each file on its own.
from mariepy.body import VoxelBody
from mariepy.coil import Port, SurfaceCoil
from mariepy.constants import Medium
from mariepy.mesh import SurfaceMesh
from mariepy.solver import solve
medium = Medium(3.0) # 1H at 3 T
body = VoxelBody.sphere(0.07, 0.005, 52.0, 0.55, padding=2)
coil = SurfaceCoil.build(
SurfaceMesh.loop(radius=0.12, width=0.01, n_around=48, n_across=2),
(Port(tag=1, kind="port", load="none", value=0.0, quality=1.0, voltage=1.0),),
)
result = solve(body, coil, medium)result.impedance, result.admittance and result.scattering are the port
matrices; result.fields.electric and result.fields.magnetic are each port's
field over the body grid, shaped (n_ports, 3, n1, n2, n3). From there:
from mariepy.fields import absorbed_power, circular_components
watts = absorbed_power(result.operator, result.fields)
b1_plus, b1_minus = circular_components(result.operator, result.fields)A wire coil, WireCoil.read_gmsh22 or WireCoil.loop, goes to solve in the
same place, and so does a CombinedCoil of a wire coil and a surface coil; a
simulation file naming a WireFile, with or without a CoilFile, reads into
one of them.
linear=True gives the body the piecewise-linear basis, twelve unknowns per
voxel, which carries the field's variation inside each voxel; the fields then
come back as those coefficients, and fields.at_centres gives their values at
the voxel centres.
The solve runs on either device: build the body and the coil with
device="cuda" and everything downstream follows.
A coil's lumped elements are closed by co-simulation, as MARIE does it. Solve
the coil with its tunable elements opened into ports ("TMD": 1 in the
simulation file), then search their values, the matching networks' and the
decoupling, and calibrate the fields to the wave driving each matched port:
from mariepy.cosim import calibrate, co_simulate
closed = co_simulate(case.network, result.admittance, case.medium.angular_frequency)
electric = calibrate(result.fields.electric, closed.transmit)With "TMD": 0 the file's values are placed as they are. closed.transmit
has a column per transmitting port and closed.receive one per receiving
port, as the element file assigns Tx, Rx and TxRx; closed.scattering
is the transmitting ports' reflection and coupling; cosim.sweep gives the
matched ports across a band. The searches need scipy:
pip install "mariepy[cosim]".
A body's field basis is built once from a support surface around it
(basis.surface_basis) or from a shell of currents around it
(basis.dipole_basis), and any coil near that support is then solved through
it:
from mariepy import basis, metrics, plot
from mariepy.solver import assemble_coil
incident = basis.surface_basis(case.body, case.basis_support, case.medium)
solved = basis.solve(incident, case.body, case.medium)
system = assemble_coil(case.coil, case.medium)
reduced = basis.solve_coil(case.coil, system, solved, case.body, case.medium)
ultimate_snr, ultimate_efficiency = basis.ultimate_maps(solved, case.body, case.medium)metrics.noise_covariance, metrics.snr, metrics.transmit_efficiency and
metrics.g_factor map a coil's performance from its fields; plot.geometry,
plot.coil_currents, plot.scattering, plot.sweep and plot.slices draw
the model and the maps (pip install "mariepy[plot]").
See CONTRIBUTING.md.
Copyright (C) 2026 Matteo Cencini. mariepy is free software under the GNU General Public License, version 3 or any later version; see LICENSE. Code it carries from other projects, and their notices, are listed in THIRD_PARTY.md.
The licence covers the program. The files it writes, such as VOP files and
field maps, are the user's and carry whatever terms their body models impose
(see PLAN.md), so projects under any licence can read them.