Commit bd069d6c authored by Jose Marquez Prieto's avatar Jose Marquez Prieto
Browse files

Merge branch 'perovskite_database' into 'develop'

Perovskite Solar Cell database schema with ELN functionality

See merge request !749
parents 3ce46267 9aedb40c
Pipeline #137601 failed with stages
in 86 minutes and 14 seconds
......@@ -132,6 +132,7 @@ const Quantity = React.memo((props) => {
withClipboard,
ellipsisFront,
hideIfUnavailable,
maxWidth,
format
} = {...presets, ...props}
......@@ -236,7 +237,7 @@ const Quantity = React.memo((props) => {
return <div className={row ? styles.row : (column ? styles.column : styles.flex)} data-testid={`quantity-${def?.name}`}>{children}</div>
} else {
return (
<div className={styles.root} data-testid={noLabel ? undefined : `quantity-${def?.name}`}>
<div className={styles.root} style={{maxWidth: maxWidth}} data-testid={noLabel ? undefined : `quantity-${def?.name}`}>
{!noLabel ? <Typography
noWrap
classes={{root: styles.label}}
......@@ -303,7 +304,12 @@ Quantity.propTypes = {
ellipsisFront: PropTypes.bool,
hideIfUnavailable: PropTypes.bool,
description: PropTypes.string,
format: PropTypes.bool
format: PropTypes.bool,
maxWidth: PropTypes.string
}
Quantity.defaultProps = {
maxWidth: '280px'
}
export default Quantity
......@@ -503,6 +509,7 @@ export const QuantityCell = React.memo(({
hideIfUnavailable,
format,
children,
maxWidth,
...other
}) => {
const context = useContext(quantityTableContext)
......@@ -523,6 +530,7 @@ export const QuantityCell = React.memo(({
format={format}
noWrap
data={finalData}
maxWidth={maxWidth}
/>}
</TableCell>
})
......@@ -533,6 +541,8 @@ QuantityCell.propTypes = {
data: PropTypes.object,
label: PropTypes.string,
description: PropTypes.string,
options: PropTypes.object,
maxWidth: PropTypes.string,
className: PropTypes.string,
hideIfUnavailable: PropTypes.bool,
format: PropTypes.bool,
......
......@@ -20,6 +20,7 @@ import PropTypes from 'prop-types'
import { Typography, makeStyles, Box, Grid, Divider } from '@material-ui/core'
import Quantity from '../Quantity'
import ElectronicPropertiesCard from '../entry/properties/ElectronicPropertiesCard'
import OptoelectronicPropertiesCard from '../entry/properties/OptoelectronicPropertiesCard'
import MaterialCard from '../entry/properties/MaterialCard'
import MaterialCardTopology from '../entry/properties/MaterialCardTopology'
import NexusCard from './properties/NexusCard'
......@@ -206,6 +207,7 @@ const OverviewView = React.memo((props) => {
: <MaterialCard index={index} archive={archive} properties={properties}/>
}
<ElectronicPropertiesCard index={index} archive={archive} properties={properties}/>
<OptoelectronicPropertiesCard index={index} archive={archive} properties={properties}/>
<VibrationalPropertiesCard index={index} archive={archive} properties={properties}/>
<MechanicalPropertiesCard index={index} archive={archive} properties={properties}/>
<ThermodynamicPropertiesCard index={index} archive={archive} properties={properties}/>
......
/*
* Copyright The NOMAD Authors.
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react'
import PropTypes from 'prop-types'
import { PropertyCard } from './PropertyCard'
import OptoelectronicProperties from '../../visualization/OptoelectronicProperties'
const OptoelectronicPropertiesCard = React.memo(({index, properties, archive}) => {
// Find out which properties are present
const hasBg = properties.has('optoelectronic.band_gap')
const hasSc = properties.has('solar_cell')
// Do not show the card if none of the properties are available
if (!hasBg && !hasSc) {
return null
}
// Resolve band gap data
const bandGap = index?.results?.properties?.optoelectronic?.band_gap || false
// Resolve solar cell data
const solarCell = index?.results?.properties?.optoelectronic?.solar_cell || false
return <PropertyCard title="Optoelectronic properties">
<OptoelectronicProperties bandGap={bandGap} solarCell={solarCell}/>
</PropertyCard>
})
OptoelectronicPropertiesCard.propTypes = {
index: PropTypes.object.isRequired,
properties: PropTypes.object.isRequired,
archive: PropTypes.object
}
export default OptoelectronicPropertiesCard
......@@ -44,6 +44,7 @@ export const labelExperiment = 'Experiment'
export const labelEELS = 'EELS'
export const labelProperties = 'Properties'
export const labelElectronic = 'Electronic'
export const labelOptoelectronic = 'Optoelectronic'
export const labelVibrational = 'Vibrational'
export const labelMechanical = 'Mechanical'
export const labelSpectroscopy = 'Spectroscopy'
......@@ -283,6 +284,7 @@ const termQuantityBool = {
}
const termQuantityNonExclusive = {aggs: {terms: {size: 5}}, exclusive: false}
const termQuantityAll = {aggs: {terms: {size: 5}}, exclusive: false, multiple: true, queryMode: 'all'}
const termQuantityAllNonExclusive = {...termQuantityNonExclusive, queryMode: 'all'}
const noAggQuantity = {}
const nestedQuantity = {}
const noQueryQuantity = {guiOnly: true, multiple: false}
......@@ -368,6 +370,36 @@ registerFilter(
{name: 'value', ...numberHistogramQuantity, scale: '1/4'}
]
)
registerFilter(
'results.properties.optoelectronic.band_gap',
labelOptoelectronic,
nestedQuantity,
[
{name: 'type', ...termQuantity},
{name: 'value', ...numberHistogramQuantity, scale: '1/4'}
]
)
registerFilter(
'results.properties.optoelectronic.solar_cell',
labelOptoelectronic,
nestedQuantity,
[
{name: 'efficiency', ...numberHistogramQuantity, scale: '1/4'},
{name: 'fill_factor', ...numberHistogramQuantity, scale: '1/4'},
{name: 'open_circuit_voltage', ...numberHistogramQuantity, scale: '1/4'},
{name: 'short_circuit_current_density', ...numberHistogramQuantity, scale: '1/4'},
{name: 'illumination_intensity', ...numberHistogramQuantity, scale: '1/4'},
{name: 'device_area', ...numberHistogramQuantity, scale: '1/4'},
{name: 'device_architecture', ...termQuantity},
{name: 'absorber_fabrication', ...termQuantity},
{name: 'device_stack', ...termQuantityAllNonExclusive},
{name: 'absorber', ...termQuantityAllNonExclusive},
{name: 'electron_transport_layer', ...termQuantityAllNonExclusive},
{name: 'hole_transport_layer', ...termQuantityAllNonExclusive},
{name: 'substrate', ...termQuantityAllNonExclusive},
{name: 'back_contact', ...termQuantityAllNonExclusive}
]
)
registerFilter(
'results.properties.mechanical.bulk_modulus',
labelMechanical,
......@@ -474,6 +506,19 @@ registerFilterOptions(
}
)
// Optoelectronic properties: subset of results.properties.available_properties
registerFilterOptions(
'optoelectronic_properties',
labelOptoelectronic,
'results.properties.available_properties',
'Optoelectronic properties',
'The optoelectronic properties that are present in an entry.',
{
'optoelectronic.band_gap': {label: 'Band gap'},
'solar_cell': {label: 'Solar cell'}
}
)
// Vibrational properties: subset of results.properties.available_properties
registerFilterOptions(
'vibrational_properties',
......
......@@ -33,6 +33,7 @@ import FilterSubMenuDFT from './FilterSubMenuDFT'
import FilterSubMenuGW from './FilterSubMenuGW'
import FilterSubMenuEELS from './FilterSubMenuEELS'
import FilterSubMenuElectronic from './FilterSubMenuElectronic'
import FilterSubMenuOptoElectronic from './FilterSubMenuOptoelectronic'
import FilterSubMenuVibrational from './FilterSubMenuVibrational'
import FilterSubMenuMechanical from './FilterSubMenuMechanical'
import FilterSubMenuThermodynamic from './FilterSubMenuThermodynamic'
......@@ -56,6 +57,7 @@ import {
labelEELS,
labelProperties,
labelElectronic,
labelOptoelectronic,
labelVibrational,
labelMechanical,
labelELN,
......@@ -120,6 +122,7 @@ const FilterMainMenu = React.memo(({
<FilterMenuItem value={labelEELS} depth={2}/>
<FilterMenuItem value={labelProperties} depth={0} disableButton/>
<FilterMenuItem value={labelElectronic} depth={1}/>
<FilterMenuItem value={labelOptoelectronic} depth={1}/>
<FilterMenuItem value={labelVibrational} depth={1}/>
<FilterMenuItem value={labelMechanical} depth={1}/>
<FilterMenuItem value={labelSpectroscopy} depth={1}/>
......@@ -154,6 +157,7 @@ const FilterMainMenu = React.memo(({
<FilterSubMenuGW value={labelGW}/>
<FilterSubMenuEELS value={labelEELS}/>
<FilterSubMenuElectronic value={labelElectronic}/>
<FilterSubMenuOptoElectronic value={labelOptoelectronic}/>
<FilterSubMenuVibrational value={labelVibrational}/>
<FilterSubMenuMechanical value={labelMechanical}/>
<FilterSubMenuThermodynamic value={labelThermodynamic}/>
......
/*
* Copyright The NOMAD Authors.
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import { FilterSubMenu, filterMenuContext } from './FilterMenu'
import { InputGrid, InputGridItem } from '../input/InputGrid'
import InputSection from '../input/InputSection'
import InputRange from '../input/InputRange'
import InputField from '../input/InputField'
const FilterSubMenuOptoElectronic = React.memo(({
value,
...rest
}) => {
const {selected, open} = useContext(filterMenuContext)
const visible = open && value === selected
return <FilterSubMenu value={value} {...rest}>
<InputGrid>
<InputGridItem xs={12}>
<InputField
quantity="optoelectronic_properties"
visible={visible}
disableSearch
/>
</InputGridItem>
<InputGridItem xs={12}>
<InputSection
section="results.properties.optoelectronic.band_gap"
visible={visible}
>
<InputField
quantity="results.properties.optoelectronic.band_gap.type"
visible={visible}
disableSearch
/>
<InputRange
quantity="results.properties.optoelectronic.band_gap.value"
visible={visible}
/>
</InputSection>
</InputGridItem>
<InputGridItem xs={12}>
<InputSection
section="results.properties.optoelectronic.solar_cell"
visible={visible}
>
<InputRange
quantity="results.properties.optoelectronic.solar_cell.efficiency"
visible={visible}
/>
<InputRange
quantity="results.properties.optoelectronic.solar_cell.fill_factor"
visible={visible}
/>
<InputRange
quantity="results.properties.optoelectronic.solar_cell.open_circuit_voltage"
visible={visible}
/>
<InputRange
quantity="results.properties.optoelectronic.solar_cell.short_circuit_current_density"
visible={visible}
/>
<InputRange
quantity="results.properties.optoelectronic.solar_cell.illumination_intensity"
visible={visible}
/>
<InputRange
quantity="results.properties.optoelectronic.solar_cell.device_area"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.device_architecture"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.device_stack"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.absorber"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.absorber_fabrication"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.electron_transport_layer"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.hole_transport_layer"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.substrate"
visible={visible}
/>
<InputField
quantity="results.properties.optoelectronic.solar_cell.back_contact"
visible={visible}
/>
</InputSection>
</InputGridItem>
</InputGrid>
</FilterSubMenu>
})
FilterSubMenuOptoElectronic.propTypes = {
value: PropTypes.string
}
export default FilterSubMenuOptoElectronic
......@@ -94,7 +94,7 @@ const CreateEntry = React.memo(function CreateEntry(props) {
.then(response => {
// TODO handle processing errors
const entryId = response.processing.entry_id
history.push(getUrl(`entry/id/${entryId}`, location))
history.push(getUrl(`entry/id/${entryId}/data/data`, location))
})
.catch(raiseError)
}, [api, raiseError, selectedTemplate, name, uploadId, history, location])
......
/*
* Copyright The NOMAD Authors.
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { } from 'react'
import PropTypes from 'prop-types'
import { SectionTable } from '../Quantity'
import { useUnits } from '../../units'
import NoData from './NoData'
import Placeholder from './Placeholder'
// Band gap quantities to show. Saved as const object to prevent re-renders
const columns = {
index: {label: 'Ch.', align: 'left'},
value: {label: 'Value'},
type: {label: 'Type', placeholder: '-'}
}
/**
* Shows a summary of all band gap values, each displayed in a separate row of a
* table.
*/
const BandGap = React.memo(({data, section}) => {
const units = useUnits()
return data !== false
? data
? <SectionTable
horizontal
section={section || 'results.properties.electronic.band_structure_electronic.band_gap'}
quantities={columns}
data={{data: data}}
units={units}
/>
: <Placeholder />
: <NoData />
})
BandGap.propTypes = {
data: PropTypes.any,
section: PropTypes.string
}
export default BandGap
......@@ -22,7 +22,7 @@ import { Quantity } from '../../units'
import DOS from './DOS'
import BandStructure from './BandStructure'
import BrillouinZone from './BrillouinZone'
import { SectionTable } from '../Quantity'
import BandGap from './BandGap'
import { makeStyles } from '@material-ui/core/styles'
import { electronicRange } from '../../config'
import { PropertyGrid, PropertyItem } from '../entry/properties/PropertyCard'
......@@ -45,13 +45,6 @@ const useStyles = makeStyles((theme) => {
}
})
// Band gap quantities to show. Saved as const object to prevent re-renders
const bandGapQuantities = {
index: {label: 'Ch.', align: 'left'},
value: {label: 'Value'},
type: {label: 'Type', placeholder: 'no gap'}
}
const ElectronicProperties = React.memo(({
bs,
dos,
......@@ -114,13 +107,7 @@ const ElectronicProperties = React.memo(({
/>
</PropertyItem>
<PropertyItem title="Band gaps" xs={4}>
<SectionTable
horizontal
section="results.properties.electronic.band_structure_electronic.band_gap"
quantities={bandGapQuantities}
data={bs === false ? false : bs?.band_gap && {data: bs.band_gap}}
units={units}
/>
<BandGap data={bs && bs?.band_gap}/>
</PropertyItem>
</>}
</PropertyGrid>
......
/*
* Copyright The NOMAD Authors.
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react'
import PropTypes from 'prop-types'
import { PropertyGrid, PropertyItem } from '../entry/properties/PropertyCard'
import SolarCell from './SolarCell'
import BandGap from './BandGap'
// Displays the set of optoelectronic properties.
const OptoelectronicProperties = React.memo(({
bandGap,
solarCell
}) => {
return <PropertyGrid>
<PropertyItem title="Solar cell" xs={12} height="auto" minHeight="100px">
<SolarCell data={solarCell} />
</PropertyItem>
<PropertyItem title="Band gap" xs={12} height="auto" minHeight="100px">
<BandGap
section="results.properties.optoelectronic.band_gap"
data={bandGap}
/>
</PropertyItem>
</PropertyGrid>
})
OptoelectronicProperties.propTypes = {
bandGap: PropTypes.any, // Set to false if not available, set to other falsy value to show placeholder.
solarCell: PropTypes.any // Set to false if not available, set to other falsy value to show placeholder.
}
export default OptoelectronicProperties
/*
* Copyright The NOMAD Authors.
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { } from 'react'
import PropTypes from 'prop-types'
import { QuantityTable, QuantityRow, QuantityCell } from '../Quantity'
import NoData from './NoData'
import Placeholder from './Placeholder'
/**
* Shows a summary of solar cell properties.
*/
const SolarCell = React.memo(({data}) => {
const prefix = 'results.properties.optoelectronic.solar_cell'
return data !== false
? data
? <QuantityTable>
<QuantityRow>
<QuantityCell value={data?.efficiency} quantity={`${prefix}.efficiency`}/>
<QuantityCell value={data?.fill_factor} quantity={`${prefix}.fill_factor`}/>
<QuantityCell value={data?.open_circuit_voltage} quantity={`${prefix}.open_circuit_voltage`}/>
<QuantityCell value={data?.short_circuit_current_density} quantity={`${prefix}.short_circuit_current_density`}/>
</QuantityRow>
<QuantityRow>
<QuantityCell value={data?.illumination_intensity} quantity={`${prefix}.illumination_intensity`}/>
<QuantityCell value={data?.device_area} quantity={`${prefix}.device_area`}/>
<QuantityCell value={data?.device_architecture} colSpan={2} quantity={`${prefix}.device_architecture`}/>
</QuantityRow>
<QuantityRow>
<QuantityCell value={data?.absorber} colSpan={2} quantity={`${prefix}.absorber`}/>
<QuantityCell value={data?.absorber_fabrication} colSpan={2} quantity={`${prefix}.absorber_fabrication`}/>
</QuantityRow>
<QuantityRow>
<QuantityCell value={data?.device_stack} colSpan={4} maxWidth="none" quantity={`${prefix}.device_stack`}/>
</QuantityRow>
</QuantityTable>
: <Placeholder />
: <NoData />
})
SolarCell.propTypes = {
data: PropTypes.any
}
export default SolarCell
......@@ -474,6 +474,38 @@ def units(ctx):
# ones.
unit_list.sort(key=lambda x: 0 if x.get('definition') is None else 1)
# Go through the metainfo and check that all units are defined. Note that
# this will break if complex derived units are used in the metainfo. In
# this case they can only be validated in a GUI test.
unit_names = set()
for unit in unit_list:
unit_names.add(unit['name'])
for alias in unit.get('aliases', []):
unit_names.add(alias)