Skip to content
Open
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
32 changes: 21 additions & 11 deletions docs/examples/cuboids_demagnetization.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,16 @@ import magpylib as magpy
import numpy as np
import pandas as pd
import plotly.express as px
from loguru import logger
from magpylib_material_response import get_dataset
from magpylib_material_response import get_dataset, configure_logging
from magpylib_material_response.demag import apply_demag
from magpylib_material_response.meshing import mesh_all
from magpylib_material_response.logging_config import get_logger

# Configure logging to see progress messages
configure_logging()

# Initialize logger for contextualized logging
logger = get_logger("magpylib_material_response.examples.cuboids_demagnetization")

if magpy.__version__.split(".")[0] != "5":
raise RuntimeError(
Expand Down Expand Up @@ -90,15 +96,19 @@ coll_meshed.show()
# apply demagnetization with varying number of cells
colls = [coll]
for target_elems in [1, 2, 8, 16, 32, 64, 128, 256]:
with logger.contextualize(target_elems=target_elems):
coll_meshed = mesh_all(
coll, target_elems=target_elems, per_child_elems=True, min_elems=1
)
coll_demag = apply_demag(
coll_meshed,
style={"label": f"Coll_demag ({len(coll_meshed.sources_all):3d} cells)"},
)
colls.append(coll_demag)
logger.info("🔄 Processing demagnetization with {target_elems} target elements", target_elems=target_elems)

coll_meshed = mesh_all(
coll, target_elems=target_elems, per_child_elems=True, min_elems=1
)

coll_demag = apply_demag(
coll_meshed,
style={"label": f"Coll_demag ({len(coll_meshed.sources_all):3d} cells)"},
)
colls.append(coll_demag)

logger.info("✅ Completed demagnetization: {actual_cells} cells created", actual_cells=len(coll_meshed.sources_all))
```

## Compare with FEM analysis
Expand Down
31 changes: 21 additions & 10 deletions docs/examples/soft_magnets.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ import magpylib as magpy
import numpy as np
import pandas as pd
import plotly.express as px
from loguru import logger
from magpylib_material_response import configure_logging
from magpylib_material_response.demag import apply_demag
from magpylib_material_response.meshing import mesh_all
from magpylib_material_response.logging_config import get_logger

# Configure logging to see progress messages
configure_logging()

# Initialize logger for contextualized logging
logger = get_logger("magpylib_material_response.examples.soft_magnets")

magpy.defaults.display.backend = "plotly"

Expand Down Expand Up @@ -84,15 +91,19 @@ magpy.show(*coll_meshed)
# apply demagnetization with varying number of cells
colls = [coll]
for target_elems in [1, 2, 8, 16, 32, 64, 128, 256]:
with logger.contextualize(target_elems=target_elems):
coll_meshed = mesh_all(
coll, target_elems=target_elems, per_child_elems=True, min_elems=1
)
coll_demag = apply_demag(
coll_meshed,
style={"label": f"Coll_demag ({len(coll_meshed.sources_all):3d} cells)"},
)
colls.append(coll_demag)
logger.info("🔄 Processing demagnetization with {target_elems} target elements", target_elems=target_elems)

coll_meshed = mesh_all(
coll, target_elems=target_elems, per_child_elems=True, min_elems=1
)

coll_demag = apply_demag(
coll_meshed,
style={"label": f"Coll_demag ({len(coll_meshed.sources_all):3d} cells)"},
)
colls.append(coll_demag)

logger.info("✅ Completed demagnetization: {actual_cells} cells created", actual_cells=len(coll_meshed.sources_all))
```

+++ {"user_expressions": []}
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:glob: true
:maxdepth: 2

logging
examples/index
```

Expand Down
123 changes: 123 additions & 0 deletions docs/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Logging Configuration

The magpylib-material-response package uses structured logging with
[Loguru](https://loguru.readthedocs.io/) to provide informative messages about
computation progress and debugging information.

## Default Behavior

By default, the library **does not output any log messages**. This follows best
practices for Python libraries to avoid cluttering user output unless explicitly
requested.

## Enabling Logging

To see log messages from the library, you need to configure logging:

```python
from magpylib_material_response import configure_logging
from magpylib_material_response.demag import apply_demag

# Enable logging with default settings (INFO level, colored output to stderr)
configure_logging()

# Now use the library - you'll see progress messages
# ... your code here
```

## Configuration Options

### Log Level

```python
from magpylib_material_response import configure_logging

# Set to DEBUG for detailed internal operations
configure_logging(level="DEBUG")

# Set to WARNING to only see important warnings and errors
configure_logging(level="WARNING")

# Available levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
```

### Output Destination

```python
import sys
from magpylib_material_response import configure_logging

# Output to stdout instead of stderr
configure_logging(sink=sys.stdout)

# Output to a file
configure_logging(sink="/path/to/logfile.log")
```

### Disable Colors and Time

```python
from magpylib_material_response import configure_logging

# Disable colored output (useful for log files)
configure_logging(enable_colors=False)

# Disable timestamps
configure_logging(show_time=False)
```

## Environment Variables

You can also configure logging using environment variables:

```bash
# Set log level
export MAGPYLIB_LOG_LEVEL=DEBUG

# Disable colors
export MAGPYLIB_LOG_COLORS=false

# Disable timestamps
export MAGPYLIB_LOG_TIME=false
```

## Disabling Logging

To completely disable logging output:

```python
from magpylib_material_response import disable_logging

disable_logging()
```

## Example Usage

```python
import magpylib as magpy
from magpylib_material_response import configure_logging
from magpylib_material_response.demag import apply_demag
from magpylib_material_response.meshing import mesh_Cuboid

# Enable logging to see progress
configure_logging(level="INFO")

# Create a magnet
magnet = magpy.magnet.Cuboid(dimension=(0.01, 0.01, 0.02), polarization=(0, 0, 1))
magnet.susceptibility = 0.1

# Mesh the magnet - you'll see meshing progress
meshed = mesh_Cuboid(magnet, target_elems=1000, verbose=True)

# Apply demagnetization - you'll see computation progress
apply_demag(meshed, inplace=True)
```

This will output structured log messages showing the progress of operations,
timing information, and any warnings or errors.

## See Also

- {doc}`examples/index` - Working examples that demonstrate logging output
- [Loguru Documentation](https://loguru.readthedocs.io/) - Complete reference
for the underlying logging library
3 changes: 2 additions & 1 deletion src/magpylib_material_response/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from __future__ import annotations

from magpylib_material_response._data import get_dataset
from magpylib_material_response.logging_config import configure_logging, disable_logging

from ._version import version as __version__

__all__ = ["__version__", "get_dataset"]
__all__ = ["__version__", "configure_logging", "disable_logging", "get_dataset"]
64 changes: 29 additions & 35 deletions src/magpylib_material_response/demag.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,18 @@

from __future__ import annotations

import sys
from collections import Counter

import magpylib as magpy
import numpy as np
from loguru import logger
from magpylib._src.obj_classes.class_BaseExcitations import BaseCurrent, BaseMagnet
from magpylib.magnet import Cuboid
from scipy.spatial.transform import Rotation as R

from magpylib_material_response.logging_config import get_logger
from magpylib_material_response.utils import timelog

config = {
"handlers": [
{
"sink": sys.stdout,
"colorize": True,
"format": (
"<magenta>{time:YYYY-MM-DD at HH:mm:ss}</magenta>"
" | <level>{level:^8}</level>"
" | <cyan>{function}</cyan>"
" | <yellow>{extra}</yellow> {level.icon:<2} {message}"
),
},
],
}
logger.configure(**config)
logger = get_logger("magpylib_material_response.demag")


def get_susceptibilities(sources, susceptibility=None):
Expand Down Expand Up @@ -224,7 +209,9 @@ def demag_tensor(
H_unit_pol = []
for split_ind, src_list_subset in enumerate(src_list_split):
logger.info(
f"Sources subset {split_ind + 1}/{len(src_list_split)}"
"Sources subset {subset_num}/{total_subsets}",
subset_num=split_ind + 1,
total_subsets=len(src_list_split),
)
if src_list_subset.size > 0:
H_unit_pol.append(
Expand Down Expand Up @@ -272,14 +259,16 @@ def filter_distance(
"dimension": np.repeat(dim0, len(src_list), axis=0)[mask],
}
dsf = sum(mask) / len(mask) * 100
log_msg = (
"Interaction pairs left after distance factor filtering: "
f"<blue>{dsf:.2f}%</blue>"
)
if dsf == 0:
logger.opt(colors=True).warning(log_msg)
logger.warning(
"No interaction pairs left after distance factor filtering",
percentage=f"{dsf:.2f}%",
)
else:
logger.opt(colors=True).success(log_msg)
logger.info(
"Interaction pairs left after distance factor filtering",
percentage=f"{dsf:.2f}%",
)
out = [mask]
if return_params:
out.append(params)
Expand All @@ -304,25 +293,25 @@ def match_pairs(src_list, min_log_time=1):
len_src = len(src_list)
num_of_pairs = len_src**2
with logger.contextualize(task="Match interactions pairs"):
logger.info("position")
logger.debug("Computing position differences")
pos2 = np.tile(pos0, (len_src, 1)) - np.repeat(pos0, len_src, axis=0)
logger.info("orientation")
logger.debug("Computing orientation differences")
rotQ2a = np.tile(rotQ0, (len_src, 1)).reshape((num_of_pairs, -1))
rotQ2b = np.repeat(rotQ0, len_src, axis=0).reshape((num_of_pairs, -1))
logger.info("dimension")
logger.debug("Computing dimension differences")
dim2 = np.tile(dim0, (len_src, 1)) - np.repeat(dim0, len_src, axis=0)
logger.info("concatenate properties")
logger.debug("Concatenating properties for comparison")
prop = (np.concatenate([pos2, rotQ2a, rotQ2b, dim2], axis=1) + 1e-9).round(
8
)
logger.info("find unique indices")
logger.debug("Finding unique interaction pairs")
_, unique_inds, unique_inv_inds = np.unique(
prop, return_index=True, return_inverse=True, axis=0
)
perc = len(unique_inds) / len(unique_inv_inds) * 100
logger.opt(colors=True).info(
"Interaction pairs left after pair matching filtering: "
f"<blue>{perc:.2f}%</blue>"
logger.info(
"Interaction pairs left after pair matching filtering",
percentage=f"{perc:.2f}%",
)

params = {
Expand Down Expand Up @@ -408,20 +397,25 @@ def apply_demag(
if not isinstance(src, BaseMagnet | BaseCurrent | magpy.Sensor)
]
if others_list:
counts_others = Counter(s.__class__.__name__ for s in others_list)
counts_str = ", ".join(
f"{count} {name}" for name, count in counts_others.items()
)
msg = (
"Only Magnet and Current sources supported. "
"Incompatible objects found: "
f"{Counter(s.__class__.__name__ for s in others_list)}"
f"Incompatible objects found: {counts_str}"
)
raise TypeError(msg)
n = len(magnets_list)
counts = Counter(s.__class__.__name__ for s in magnets_list)
inplace_str = f"""{" (inplace)" if inplace else ""}"""
lbl = collection.style.label
coll_str = lbl if lbl else str(collection)
# Create a clean message without problematic formatting characters
counts_str = ", ".join(f"{count} {name}" for name, count in counts.items())
demag_msg = (
f"Demagnetization{inplace_str} of <blue>{coll_str}</blue>"
f" with {n} cells - {counts}"
f" with {n} cells ({counts_str})"
)
with timelog(demag_msg, min_log_time=min_log_time):
# set up mr
Expand Down
Loading