Skip to content

Commit

Permalink
handle structure=undefined in Structure and StructureCard component
Browse files Browse the repository at this point in the history
add select to switch between multiple structures on demo page
  • Loading branch information
janosh committed Apr 24, 2023
1 parent 072d57a commit 3aa160b
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 630 deletions.
135 changes: 74 additions & 61 deletions src/lib/Structure.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { atomic_colors, atomic_radii, type Structure } from './structure'
// output of pymatgen.core.Structure.as_dict()
export let structure: Structure
export let structure: Structure | undefined = undefined
// scale factor for atomic radii
export let atom_radius: number = 0.5
// whether to use the same radius for all atoms. if not, the radius will be
Expand Down Expand Up @@ -32,7 +32,7 @@
}
}
$: ({ a, b, c } = structure.lattice)
$: ({ a, b, c } = structure?.lattice ?? {})
const on_window_click =
(node: (HTMLElement | null)[], cb: () => void) => (event: MouseEvent) => {
Expand All @@ -51,69 +51,77 @@
})}
/>

<div>
<button
class="controls-toggle"
on:click={() => (show_controls = !show_controls)}
bind:this={toggle_controls_btn}
>
{show_controls ? 'Hide' : 'Show'} controls
</button>
<section bind:this={controls} class="controls" class:open={show_controls}>
<label>
Atom radius
<input type="range" min="0.1" max="2" step="0.05" bind:value={atom_radius} />
</label>
<label>
<input type="checkbox" bind:checked={same_size_atoms} />
Scale atoms according to atomic radius (if false, all atoms have same size)
</label>
<label>
Show lattice matrix as
<select bind:value={show_cell}>
<option value="surface">surface</option>
<option value="wireframe">wireframe</option>
<option value={null}>none</option>
</select>
</label>
<label>
Cell opacity
<input type="range" min="0" max="1" step="0.01" bind:value={cell_opacity} />
</label>
</section>
{#if structure?.sites}
<div>
<button
class="controls-toggle"
on:click={() => (show_controls = !show_controls)}
bind:this={toggle_controls_btn}
>
{show_controls ? 'Hide' : 'Show'} controls
</button>
<section bind:this={controls} class="controls" class:open={show_controls}>
<label>
Atom radius
<input type="range" min="0.1" max="2" step="0.05" bind:value={atom_radius} />
</label>
<label>
<input type="checkbox" bind:checked={same_size_atoms} />
Scale atoms according to atomic radius (if false, all atoms have same size)
</label>
<label>
Show lattice matrix as
<select bind:value={show_cell}>
<option value="surface">surface</option>
<option value="wireframe">wireframe</option>
<option value={null}>none</option>
</select>
</label>
{#if show_cell}
<label>
Cell opacity
<input type="range" min="0" max="1" step="0.05" bind:value={cell_opacity} />
</label>
{/if}
</section>

<Canvas>
<T.PerspectiveCamera makeDefault position={camera_position} fov={zoom}>
<OrbitControls enableZoom enablePan target={{ x: a / 2, y: b / 2, z: c / 2 }} />
</T.PerspectiveCamera>
<Canvas>
<T.PerspectiveCamera makeDefault position={camera_position} fov={zoom}>
<OrbitControls enableZoom enablePan target={{ x: a / 2, y: b / 2, z: c / 2 }} />
</T.PerspectiveCamera>

<T.DirectionalLight position={[3, 10, 10]} />
<T.DirectionalLight position={[-3, 10, -10]} intensity={0.2} />
<T.AmbientLight intensity={0.2} />
<T.DirectionalLight position={[3, 10, 10]} />
<T.DirectionalLight position={[-3, 10, -10]} intensity={0.2} />
<T.AmbientLight intensity={0.2} />

{#each structure.sites as { xyz, species }}
{@const symbol = species[0].element}
{@const radius = (same_size_atoms ? 1 : atomic_radii[symbol]) * atom_radius}
<T.Mesh position={xyz}>
<T.SphereGeometry args={[radius, 20, 20]} />
<T.MeshStandardMaterial
color="rgb({atomic_colors[symbol].map((x) => Math.floor(x * 255)).join(',')})"
/>
</T.Mesh>
{/each}
{#each structure?.sites ?? [] as { xyz, species }}
{@const symbol = species[0].element}
{@const radius = (same_size_atoms ? 1 : atomic_radii[symbol]) * atom_radius}
<T.Mesh position={xyz}>
<T.SphereGeometry args={[radius, 20, 20]} />
<T.MeshStandardMaterial
color="rgb({atomic_colors[symbol].map((x) => Math.floor(x * 255)).join(',')})"
/>
</T.Mesh>
{/each}

{#if show_cell}
<T.Mesh position={[a / 2, b / 2, c / 2]}>
<T.BoxGeometry args={[a, b, c]} />
<T.MeshBasicMaterial
transparent
opacity={cell_opacity ?? (show_cell === 'surface' ? 0.2 : 1)}
wireframe={show_cell === 'wireframe'}
/>
</T.Mesh>
{/if}
</Canvas>
</div>
{#if show_cell}
<T.Mesh position={[a / 2, b / 2, c / 2]}>
<T.BoxGeometry args={[a, b, c]} />
<T.MeshBasicMaterial
transparent
opacity={cell_opacity ?? (show_cell == 'surface' ? 0.2 : 1)}
wireframe={show_cell == 'wireframe'}
/>
</T.Mesh>
{/if}
</Canvas>
</div>
{:else if structure}
<p class="warn">No sites found in structure</p>
{:else}
<p class="warn">No pymatgen <code>Structure</code> provided</p>
{/if}

<style>
div {
Expand Down Expand Up @@ -154,4 +162,9 @@
/* transparent */
background-color: rgba(255, 255, 255, 0.2);
}
p.warn {
font-size: larger;
text-align: center;
}
</style>
77 changes: 43 additions & 34 deletions src/lib/StructureCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export let structure: Structure
export let title: string = ''
export let fallback: string = ''
$: ({ volume, a, b, c, alpha, beta, gamma } = structure?.lattice ?? {})
</script>
Expand All @@ -16,41 +17,49 @@
</slot>
</h2>
{/if}
<strong>
formula:
<span class="value">{alphabetical_formula(structure)}</span>
</strong>
<strong>
Number of atoms:
<span class="value">{structure?.sites.length}</span>
</strong>
<strong>
Volume:
<span class="value">
{pretty_num(volume, '.1f')} ų
<small>
&nbsp; ({pretty_num(volume / structure?.sites.length, '.1f')} ų/atom)
</small></span
>
</strong>
<strong>
Density:
<span class="value">{density(structure)} g/cm³</span>
</strong>
<strong>
Lattice lengths (a, b, c):
<span class="value">{pretty_num(a)} Å, {pretty_num(b)} Å, {pretty_num(c)} Å</span>
</strong>
<strong>
Lattice angles (α, β, γ):
<span class="value">
{pretty_num(alpha)}°, {pretty_num(beta)}°, {pretty_num(gamma)}°
</span>
</strong>
{#if structure?.charge}
{#if structure?.sites}
<strong>
Charge:
<span class="value">{pretty_num(structure?.charge)}</span>
formula:
<span class="value">{alphabetical_formula(structure)}</span>
</strong>
<strong>
Number of atoms:
<span class="value">{structure?.sites.length}</span>
</strong>
<strong>
Volume:
<span class="value">
{pretty_num(volume, '.1f')} ų
<small>
&nbsp; ({pretty_num(volume / structure?.sites.length, '.1f')} ų/atom)
</small></span
>
</strong>
<strong>
Density:
<span class="value">{density(structure)} g/cm³</span>
</strong>
<strong>
Lattice lengths (a, b, c):
<span class="value">{pretty_num(a)} Å, {pretty_num(b)} Å, {pretty_num(c)} Å</span>
</strong>
<strong>
Lattice angles (α, β, γ):
<span class="value">
{pretty_num(alpha)}°, {pretty_num(beta)}°, {pretty_num(gamma)}°
</span>
</strong>
{#if structure?.charge}
<strong>
Charge:
<span class="value">{pretty_num(structure?.charge)}</span>
</strong>
{/if}
{:else if fallback}
<strong>
<slot name="fallback">
{fallback}
</slot>
</strong>
{/if}
</div>
Expand Down
61 changes: 44 additions & 17 deletions src/routes/(demos)/structure/+page.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,56 @@
<script>
import struct_json from './mp-1234.json?raw'
</script>

## `Structure.svelte`

```svelte example stackblitz code_above
```svelte example stackblitz code_above hideStyle
<script>
import { Structure, StructureCard } from '$lib'
import structure from './mp-1234.json'
import Select from 'svelte-multiselect'
const structs = import.meta.glob('./mp-*.json', { eager: true, as: 'raw' })
const mp_id = 'mp-1234'
const href = `https://materialsproject.org/materials/${mp_id}`
let mp_id = ['mp-1']
$: href = `https://materialsproject.org/materials/${mp_id[0]}`
$: structure = JSON.parse(structs[`./${mp_id[0]}.json`] ?? '{}')
</script>
<form>
<label for="select">Select a structure:</label>
<Select
id="select"
options={Object.keys(structs).map((k) => k.slice(2, -5))}
bind:selected={mp_id}
maxSelect={1}
minSelect={1}
/>
<details>
<summary>JSON for structure {mp_id[0]}</summary>
<pre>
<code>
{JSON.stringify(structure, null, 2)}
</code>
</pre>
</details>
</form>
<StructureCard {structure}>
<a slot="title" {href}>{mp_id}</a>
</StructureCard>
<Structure {structure} />
```
<details>
<summary>JSON Structure for mp-1234</summary>
<pre>
<code>
{struct_json}
</code>
</pre>
</details>
<style>
form {
display: flex;
gap: 1em;
position: relative;
align-items: center;
}
details > pre {
position: absolute;
top: 2em;
left: 0;
background: black;
width: calc(100cqw - 2em);
z-index: 2;
}
</style>
```
1 change: 1 addition & 0 deletions src/routes/(demos)/structure/mp-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"@module": "pymatgen.core.structure", "@class": "Structure", "charge": 0, "lattice": {"matrix": [[6.256930122878799, 0.0, 3.831264723736088e-16], [1.0061911048045417e-15, 6.256930122878799, 3.831264723736088e-16], [0.0, 0.0, 6.256930122878799]], "pbc": [true, true, true], "a": 6.256930122878799, "b": 6.256930122878799, "c": 6.256930122878799, "alpha": 90.0, "beta": 90.0, "gamma": 90.0, "volume": 244.95364960649798}, "sites": [{"species": [{"element": "Cs", "occu": 1}], "abc": [0.0, 0.0, 0.0], "xyz": [0.0, 0.0, 0.0], "label": "Cs", "properties": {}}, {"species": [{"element": "Cs", "occu": 1}], "abc": [0.5, 0.5, 0.5], "xyz": [3.1284650614394, 3.1284650614393996, 3.1284650614394], "label": "Cs", "properties": {}}]}

0 comments on commit 3aa160b

Please sign in to comment.