Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding materials to DEMO #3680

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
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
25 changes: 22 additions & 3 deletions bluemira/builders/coil_supports.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
sweep_shape,
)
from bluemira.geometry.wire import BluemiraWire
from bluemira.materials.cache import get_cached_material
from bluemira.optimisation import OptimisationProblem
from bluemira.utilities.tools import floatify

Expand Down Expand Up @@ -283,7 +284,13 @@ def build_xyz(
# Finally, make the floor block
shape_list.append(self._make_floor_block(floatify(v1.x), floatify(v4.x)))
shape = boolean_fuse(shape_list)
component = PhysicalComponent("ITER-like gravity support", shape)
component = PhysicalComponent(
"ITER-like gravity support",
shape,
material=get_cached_material(
self.build_config.get("material", {}).get("GS")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts welcome:
Possibly build_config['material'] should be autocreated in the reactor config and then the materials entry could be a default dict https://docs.python.org/3/library/collections.html#collections.defaultdict. It might mean we can chain gets. Also could well have unintended side effects...probably a bad idea.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component material maybe should go as a class var so it can be accessed outside? Same as we do in some places for the PhysicalComponent name (and you've done below).

),
)
apply_component_display_options(component, color=BLUE_PALETTE["TF"][2])
return component

Expand Down Expand Up @@ -341,7 +348,13 @@ def build_xz(self, xyz):
result = slice_shape(xyz.shape, BluemiraPlane(axis=(0, 1, 0)))
result.sort(key=lambda wire: -wire.length)
face = BluemiraFace(result)
component = PhysicalComponent(self.name, face)
component = PhysicalComponent(
self.name,
face,
material=get_cached_material(
self.build_config.get("material", {}).get("PF ICS")
),
)
apply_component_display_options(component, color=BLUE_PALETTE["TF"][2])
return component

Expand Down Expand Up @@ -866,7 +879,13 @@ def build_xz(self):
components = []
for i, ois_profile in enumerate(self.ois_xz_profiles):
face = BluemiraFace(ois_profile)
component = PhysicalComponent(f"{self.OIS_XZ} {i}", face)
component = PhysicalComponent(
f"{self.OIS_XZ} {i}",
face,
material=get_cached_material(
self.build_config.get("material", {}).get(self.OIS_XZ)
),
)
apply_component_display_options(component, color=BLUE_PALETTE["TF"][2])
components.append(component)
return components
Expand Down
4 changes: 4 additions & 0 deletions bluemira/builders/cryostat.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from bluemira.display.palettes import BLUE_PALETTE
from bluemira.geometry.face import BluemiraFace
from bluemira.geometry.tools import make_polygon
from bluemira.materials.cache import get_cached_material

if TYPE_CHECKING:
from bluemira.base.parameter_frame.typed import ParameterFrameLike
Expand Down Expand Up @@ -209,4 +210,7 @@ def build_xyz(
self.params.n_TF.value,
BLUE_PALETTE["CR"][0],
degree,
material=get_cached_material(
self.build_config.get("material", {}).get("Body"),
),
)
7 changes: 6 additions & 1 deletion bluemira/builders/divertor.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
pattern_revolved_silhouette,
)
from bluemira.display.palettes import BLUE_PALETTE
from bluemira.materials.cache import get_cached_material

if TYPE_CHECKING:
from bluemira.base.builder import BuildConfig
Expand Down Expand Up @@ -95,7 +96,11 @@ def build_xyz(self, degree: float = 360.0) -> list[PhysicalComponent]:

segments = []
for no, shape in enumerate(shapes):
segment = PhysicalComponent(f"{self.SEGMENT_PREFIX}_{no}", shape)
segment = PhysicalComponent(
f"{self.SEGMENT_PREFIX}_{no}",
shape,
material=get_cached_material(self.build_config.get("material", {})),
)
apply_component_display_options(segment, BLUE_PALETTE[self.DIV][no])
segments.append(segment)

Expand Down
27 changes: 23 additions & 4 deletions bluemira/builders/pf_coil.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from bluemira.geometry.face import BluemiraFace
from bluemira.geometry.parameterisations import PictureFrame
from bluemira.geometry.tools import make_circle, offset_wire, revolve_shape
from bluemira.materials.cache import get_cached_material

if TYPE_CHECKING:
from bluemira.base.builder import BuildConfig
Expand Down Expand Up @@ -120,16 +121,34 @@ def build_xz(self, shape: BluemiraWire) -> list[PhysicalComponent]:
"""
Build the xz cross-section of the PF coil.
"""
wp = PhysicalComponent(self.WINDING_PACK, BluemiraFace(shape))
wp = PhysicalComponent(
self.WINDING_PACK,
BluemiraFace(shape),
material=get_cached_material(
self.build_config.get("material", {}).get(self.WINDING_PACK)
),
)
idx = CoilType(self.params.ctype.value).value - 1
apply_component_display_options(wp, color=BLUE_PALETTE["PF"][idx])

ins_shape = offset_wire(shape, self.params.tk_insulation.value)
ins = PhysicalComponent(self.GROUND_INSULATION, BluemiraFace([ins_shape, shape]))
ins = PhysicalComponent(
self.GROUND_INSULATION,
BluemiraFace([ins_shape, shape]),
material=get_cached_material(
self.build_config.get("material", {}).get(self.GROUND_INSULATION)
),
)
apply_component_display_options(ins, color=BLUE_PALETTE["PF"][3])

cas_shape = offset_wire(ins_shape, self.params.tk_casing.value)
casing = PhysicalComponent(self.CASING, BluemiraFace([cas_shape, ins_shape]))
casing = PhysicalComponent(
self.CASING,
BluemiraFace([cas_shape, ins_shape]),
material=get_cached_material(
self.build_config.get("material", {}).get(self.CASING)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe also we could have a wrapper function that does some of this so the user only passes in a dictionary and a key, possibly harder to work out whats going on:

get_cached_material_for_component(self.build_config, self.CASING)

),
)
apply_component_display_options(casing, color=BLUE_PALETTE["PF"][2])
return [wp, ins, casing]

Expand Down Expand Up @@ -159,7 +178,7 @@ def build_xyz(
components = []
for c in xz_components:
shape = revolve_shape(c.shape, degree=sector_degree * n_sectors)
c_xyz = PhysicalComponent(c.name, shape)
c_xyz = PhysicalComponent(c.name, shape, material=c.material)
apply_component_display_options(
c_xyz, color=c.plot_options.face_options["color"]
)
Expand Down
2 changes: 2 additions & 0 deletions bluemira/builders/radiation_shield.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from bluemira.display.palettes import BLUE_PALETTE
from bluemira.geometry.face import BluemiraFace
from bluemira.geometry.tools import boolean_cut, boolean_fuse, make_polygon, offset_wire
from bluemira.materials.cache import get_cached_material

if TYPE_CHECKING:
from bluemira.base.builder import BuildConfig
Expand Down Expand Up @@ -130,4 +131,5 @@ def build_xyz(
self.params.n_TF.value,
BLUE_PALETTE[self.RS][0],
degree,
material=get_cached_material(self.build_config.get("material", {})),
)
16 changes: 13 additions & 3 deletions bluemira/builders/thermal_shield.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
make_polygon,
offset_wire,
)
from bluemira.materials.cache import Void
from bluemira.materials.cache import Void, get_cached_material

if TYPE_CHECKING:
from bluemira.base.builder import BuildConfig
Expand Down Expand Up @@ -159,7 +159,12 @@ def build_xyz(
self.params.n_TF.value,
[BLUE_PALETTE["TS"][0], (0, 0, 0)],
degree,
material=[None, Void("vacuum")],
material=[
get_cached_material(
self.build_config.get("material", {}).get(self.VVTS)
),
Void("vacuum"),
],
)


Expand Down Expand Up @@ -312,5 +317,10 @@ def build_xyz(
[BLUE_PALETTE["TS"][0], (0, 0, 0)],
degree,
enable_sectioning=True,
material=[None, Void("vacuum")],
material=[
get_cached_material(
self.build_config.get("material", {}).get(self.CRYO_TS)
),
Void("vacuum"),
],
)
1 change: 1 addition & 0 deletions bluemira/builders/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def circular_pattern_component(
f"with search index: {search_index_i}"
)
phy_comp.shape = shape
phy_comp.material = comp.material

return sectors

Expand Down
81 changes: 75 additions & 6 deletions bluemira/materials/cache.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like these changes, nice!

Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@

import copy
import json
from typing import Any, ClassVar
from typing import TYPE_CHECKING, Any, ClassVar

from bluemira.materials.material import (
BePebbleBed,
Liquid,
MassFractionMaterial,
Material,
MaterialsError,
NbSnSuperconductor,
NbTiSuperconductor,
Expand All @@ -27,6 +28,9 @@
)
from bluemira.materials.mixtures import HomogenisedMixture

if TYPE_CHECKING:
from pathlib import Path


class MaterialCache:
"""
Expand Down Expand Up @@ -56,6 +60,22 @@
mat_class.__name__: mat_class for mat_class in self.default_classes
}

_instance: MaterialCache | None = None

@classmethod
def get_instance(cls) -> MaterialCache:
"""
Get the singleton instance of the MaterialCache.

Returns
-------
MaterialCache
The singleton instance of the MaterialCache.
"""
if cls._instance is None:
cls._instance = MaterialCache()
return cls._instance

Check warning on line 77 in bluemira/materials/cache.py

View check run for this annotation

Codecov / codecov/patch

bluemira/materials/cache.py#L75-L77

Added lines #L75 - L77 were not covered by tests

def __getattr__(self, value: str):
"""Allow attribute access to cached materials

Expand All @@ -79,14 +99,11 @@
----------
path:
The path to the file from which to load the materials.

Returns
-------
The dictionary containing the loaded materials.
"""
with open(path) as fh:
mats_dict = json.load(fh)
return {name: self.load_from_dict(name, mats_dict) for name in mats_dict}
for name in mats_dict:
self.load_from_dict(name, mats_dict)

def load_from_dict(
self, mat_name: str, mats_dict: dict[str, Any], *, overwrite: bool = True
Expand Down Expand Up @@ -181,3 +198,55 @@
"exists in the cache."
)
self._material_dict[mat_name] = mat


def establish_material_cache(materials_json_paths: list[Path | str]):
"""
Load the material data from the provided json files into the global material cache
instance.

This instance can be accessed using the `MaterialCache.get_instance()`
function.

Parameters
----------
materials_json_paths:
A list of paths to the data files to load into the material cache.

Returns
-------
The material cache.
"""
cache = MaterialCache.get_instance()
for path in materials_json_paths:
cache.load_from_file(path)
return cache

Check warning on line 223 in bluemira/materials/cache.py

View check run for this annotation

Codecov / codecov/patch

bluemira/materials/cache.py#L220-L223

Added lines #L220 - L223 were not covered by tests


def get_cached_material(
material_name: str | None, cache: MaterialCache | None = None
) -> Material | None:
"""
Get the named material from the MaterialCache.

If cache is None, the global cache instance is used.

Parameters
----------
material_name:
The name of the material to retrieve from the dictionary
cache:
The material cache to retrieve the material from. By default the global cache.

Returns
-------
The requested material.
"""
# mateiral name can also be an empty dict
# sometimes because of the dict default value used
# when calling .get
if not (material_name and isinstance(material_name, str)):
return None
if cache is None:
cache = MaterialCache.get_instance()
return cache.get_material(material_name)

Check warning on line 252 in bluemira/materials/cache.py

View check run for this annotation

Codecov / codecov/patch

bluemira/materials/cache.py#L250-L252

Added lines #L250 - L252 were not covered by tests
Loading
Loading