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

✨ NEW: _repr_pretty_ representation for ProcessBuilder #4970

Merged
merged 16 commits into from
Jan 27, 2022

Conversation

mbercx
Copy link
Member

@mbercx mbercx commented Jun 2, 2021

The representation of the ProcessBuilder (i.e. __repr__) is
difficult to read since it just prints the heavily nested mapping on one
line and doesn't show the contents of e.g. Dict nodes.

Here we add a _repr_pretty_ method to improve the readability
when using the ProcessBuilder in Jupyter notebooks and IPython.

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

This is one I've been meaning to do for a while now, but I never knew about _repr_html_. 😅

@sphuber this is adapted from a method I stole from the aiida-quantumespresso-epfl repo. 😁
@louisponet I couldn't add you as a reviewer, but have a look and let me know what you think.

Some usage examples:

  • After using the builder for the PwCalculation for a bit:
builder = code.get_builder()
builder.structure = structure
builder.parameters = orm.Dict(dict={
  'CONTROL': {
    'calculation': 'scf',  # self-consistent field
  },
  'SYSTEM': {
    'ecutwfc': 30.,  # wave function cutoff in Ry
    'ecutrho': 240.,  # density cutoff in Ry
  }
})
builder

Gives

Process class: PwCalculation
Inputs:
"code": Remote code 'qe-v6.7-pw' on localhost, pk: 1, uuid: 47a1c120-47af-4b9e-80c2-5a647c99817e
"metadata": {
    "options": {
        "stash": {}
    },
},
"parameters": {
    "CONTROL": {
        "calculation": "scf"
    },
    "SYSTEM": {
        "ecutwfc": 30.0,
        "ecutrho": 240.0
    }
},
"pseudos": {}
"structure": Si2 (pk: 3)
  • PwBaseWorkChain obtained from get_builder_from_protocol() method:
code = orm.load_code(1)
structure = orm.load_node(3)

builder = PwBaseWorkChain.get_builder_from_protocol(
    code=code,
    structure=structure,
)
builder

Gives:

Process class: PwBaseWorkChain
Inputs:
"clean_workdir": True
"kpoints_distance": 0.15
"kpoints_force_parity": False
"metadata": {}
"pw": {
    "code": Remote code 'qe-v6.7-pw' on localhost, pk: 1, uuid: 47a1c120-47af-4b9e-80c2-5a647c99817e
    "metadata": {
        "options": {
            "max_wallclock_seconds": 43200
            "resources": {
                "num_machines": 1
            },
            "withmpi": True
        },
    },
    "parameters": {
        "CONTROL": {
            "calculation": "scf",
            "forc_conv_thr": 0.0001,
            "tprnfor": true,
            "tstress": true,
            "etot_conv_thr": 2e-05
        },
        "SYSTEM": {
            "nosym": false,
            "occupations": "smearing",
            "smearing": "cold",
            "degauss": 0.01,
            "ecutwfc": 30.0,
            "ecutrho": 240.0
        },
        "ELECTRONS": {
            "electron_maxstep": 80,
            "mixing_beta": 0.4,
            "conv_thr": 4e-10
        }
    },
    "pseudos": {
        "Si": uuid: 07a2d8db-ef3f-43ce-b200-bd68a7b4c6d2 (pk: 933)
    },
    "structure": Si2 (pk: 3)
},

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

@chrisjsewell should I add something to ignore these mypy warnings in this file?

aiida/engine/processes/builder.py:97: error: Item "Port" of "Union[Port, PortNamespace]" has no attribute "serialize"  [union-attr]
aiida/engine/processes/builder.py:97: error: Item "PortNamespace" of "Union[Port, PortNamespace]" has no attribute "serialize"  [union-attr]

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

Just for reference, this is what you would get for the PwBaseWorkChain.get_builder_from_protocol() method one without this representation:

{'metadata': {}, 'pw': {'metadata': {'options': {'resources': {'num_machines': 1}, 'max_wallclock_seconds': 43200, 'withmpi': True}}, 'pseudos': {'Si': <UpfData: uuid: 07a2d8db-ef3f-43ce-b200-bd68a7b4c6d2 (pk: 933)>}, 'code': <Code: Remote code 'qe-v6.7-pw' on localhost, pk: 1, uuid: 47a1c120-47af-4b9e-80c2-5a647c99817e>, 'structure': <StructureData: uuid: e5367a26-ebcd-479d-83db-73cf56ba1539 (pk: 3)>, 'parameters': <Dict: uuid: e6b64e56-2fec-4355-9b16-725914de9d58 (unstored)>}, 'clean_workdir': <Bool: uuid: 57f303c9-8a3e-4f0e-844f-910d4a437c08 (unstored) value: True>, 'kpoints_distance': <Float: uuid: 6055b4e0-5142-4163-9861-63971be7a0a6 (unstored) value: 0.15>, 'kpoints_force_parity': <Bool: uuid: d00c33b3-b27c-41de-b4c7-8fc47352e889 (unstored) value: False>}

(quoted here to wrap the super long line)

@louisponet
Copy link

So that's what it would look like in ipython and jupyter notebooks? I love it!

@codecov
Copy link

codecov bot commented Jun 2, 2021

Codecov Report

Merging #4970 (cd91b0e) into develop (55a7bfe) will increase coverage by 4.93%.
The diff coverage is 91.31%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4970      +/-   ##
===========================================
+ Coverage    77.21%   82.13%   +4.93%     
===========================================
  Files          533      533              
  Lines        38470    38485      +15     
===========================================
+ Hits         29700    31607    +1907     
+ Misses        8770     6878    -1892     
Flag Coverage Δ
django 77.19% <91.31%> (-0.01%) ⬇️
sqlalchemy 76.52% <91.31%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
aiida/engine/processes/builder.py 92.25% <91.31%> (-0.73%) ⬇️
aiida/backends/general/migrations/utils.py 85.28% <0.00%> (+0.51%) ⬆️
aiida/engine/daemon/client.py 76.39% <0.00%> (+1.01%) ⬆️
aiida/backends/testbase.py 93.90% <0.00%> (+1.53%) ⬆️
aiida/manage/manager.py 80.77% <0.00%> (+1.65%) ⬆️
aiida/manage/tests/main.py 88.94% <0.00%> (+2.22%) ⬆️
aiida/backends/manager.py 72.23% <0.00%> (+2.78%) ⬆️
...orm/implementation/sqlalchemy/querybuilder/main.py 85.08% <0.00%> (+4.68%) ⬆️
aiida/backends/sqlalchemy/models/log.py 96.30% <0.00%> (+7.41%) ⬆️
... and 78 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 55a7bfe...cd91b0e. Read the comment docs.

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

So that's what it would look like in ipython and jupyter notebooks? I love it!

Unfortunately, not in IPython. 😞 The console doesn't support HTML, notebooks do. See here.

@louisponet
Copy link

_repr_pretty_ I guess is the way to go for that then, probably not very hard to get a similar repr there.

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

_repr_pretty_ I guess is the way to go for that then, probably not very hard to get a similar repr there.

👀 Indeed! Let me get right on that. 😁

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

@louisponet there you go! I dumped the _repr_html_ version. It wasn't really adding anything so far. Now it works in both Jupyter notebooks as well as the IPython console.

@mbercx
Copy link
Member Author

mbercx commented Jun 2, 2021

Note: the format_inputs method should probably be moved elsewhere. I think we can also use it for e.g. formatting overrides inputs, or the inputs of a process that has already run.

@mbercx mbercx changed the title ProcessBuilder: Add HTML representation ProcessBuilder: Add pretty representation 😍 Jun 3, 2021
@chrisjsewell chrisjsewell marked this pull request as draft July 21, 2021 09:37
@chrisjsewell
Copy link
Member

changing this to a draft while the tests are failing @mbercx. Get the tests passed then we can finalise this I guess 😄

@mbercx mbercx force-pushed the fix/print-process branch 2 times, most recently from f93dffc to cb0a906 Compare August 11, 2021 07:29
@mbercx
Copy link
Member Author

mbercx commented Aug 11, 2021

Hmm, so I can't seem to fix the pre-commit because if I actually try to ignore these issues:

aiida/engine/processes/builder.py:96: error: Incompatible types in assignment (expression has type "None", variable has type "Union[Port, PortNamespace]")  [assignment]
aiida/engine/processes/builder.py:98: error: Item "Port" of "Union[Port, PortNamespace]" has no attribute "serialize"  [union-attr]
aiida/engine/processes/builder.py:98: error: Item "PortNamespace" of "Union[Port, PortNamespace]" has no attribute "serialize"  [union-attr]
Found 3 errors in 1 file (checked 74 source files)

By adding # type: ignore[assignment] and # type: ignore[union-attr], mypy complains that these are not used:

aiida/engine/processes/builder.py:96: error: unused 'type: ignore' comment
aiida/engine/processes/builder.py:98: error: unused 'type: ignore' comment
Found 2 errors in 1 file (checked 1 source file)

Don't know how to fix this due to my mypy ignorance. Will look into it, but @chrisjsewell: you prob can tell me how to fix this off the top of your head? :)

@sphuber
Copy link
Contributor

sphuber commented Aug 11, 2021

y adding # type: ignore[assignment] and # type: ignore[union-attr], mypy complains that these are not used:

Might be related to this issue: #5026 I have also suffered from this a few times and have no idea where it comes from nor how to fix it.

@chrisjsewell
Copy link
Member

Will look into it, but @chrisjsewell: you prob can tell me how to fix this off the top of your head? :)

How do I never seem to have these issues 😆
I would suggest you make sure your version of mypy is up-to-date, in fact I would suggest you use tox -r -e py38-pre-commit -- --all mypy

@mbercx
Copy link
Member Author

mbercx commented Aug 12, 2021

How do I never seem to have these issues 😆
I would suggest you make sure your version of mypy is up-to-date, in fact I would suggest you use tox -r -e py38-pre-commit -- --all mypy

sigh, alright! I'll finally start looking into using tox. 😝

@mbercx mbercx added this to the v2.0.0 milestone Sep 22, 2021
@mbercx mbercx force-pushed the fix/print-process branch 2 times, most recently from c32df03 to 11aeaab Compare October 3, 2021 21:26
@mbercx mbercx reopened this Jan 26, 2022
@mbercx
Copy link
Member Author

mbercx commented Jan 26, 2022

(oops, slippery fingers! 😅 )

@mbercx
Copy link
Member Author

mbercx commented Jan 26, 2022

Actually, maybe it makes a lot more sense to output this in a YAML format... It's meant to be human readable after all. For the PwBandsWorkChain example:

See pretty representation of builder
Process class: PwBandsWorkChain
Inputs:
bands:
  metadata: {}
  pw:
    code: Plane wave Quantum ESPRESSO v6.6 - local compilation
    metadata:
      options:
        max_wallclock_seconds: 43200
        resources:
          num_machines: 1
        stash: {}
        withmpi: true
    parameters:
      CONTROL:
        calculation: scf
        etot_conv_thr: 2.0e-05
        forc_conv_thr: 0.0001
        tprnfor: true
        tstress: true
      ELECTRONS:
        conv_thr: 4.0e-10
        electron_maxstep: 80
        mixing_beta: 0.4
      SYSTEM:
        degauss: 0.01
        ecutrho: 240.0
        ecutwfc: 30.0
        nosym: false
        occupations: smearing
        smearing: cold
    pseudos:
      Si: ''
bands_kpoints_distance: 0.025
clean_workdir: true
metadata: {}
nbands_factor: 3.0
relax:
  base:
    kpoints_distance: 0.15
    kpoints_force_parity: false
    metadata: {}
    pw:
      code: Plane wave Quantum ESPRESSO v6.6 - local compilation
      metadata:
        options:
          max_wallclock_seconds: 43200
          resources:
            num_machines: 1
          stash: {}
          withmpi: true
      parameters:
        CELL:
          cell_dofree: all
          press_conv_thr: 0.5
        CONTROL:
          calculation: vc-relax
          etot_conv_thr: 2.0e-05
          forc_conv_thr: 0.0001
          tprnfor: true
          tstress: true
        ELECTRONS:
          conv_thr: 4.0e-10
          electron_maxstep: 80
          mixing_beta: 0.4
        SYSTEM:
          degauss: 0.01
          ecutrho: 240.0
          ecutwfc: 30.0
          nosym: false
          occupations: smearing
          smearing: cold
      pseudos:
        Si: ''
  base_final_scf:
    metadata: {}
    pw:
      metadata:
        options:
          stash: {}
      pseudos: {}
  max_meta_convergence_iterations: 5
  meta_convergence: true
  metadata: {}
  volume_convergence: 0.02
scf:
  kpoints_distance: 0.15
  kpoints_force_parity: false
  metadata: {}
  pw:
    code: Plane wave Quantum ESPRESSO v6.6 - local compilation
    metadata:
      options:
        max_wallclock_seconds: 43200
        resources:
          num_machines: 1
        stash: {}
        withmpi: true
    parameters:
      CONTROL:
        calculation: scf
        etot_conv_thr: 2.0e-05
        forc_conv_thr: 0.0001
        tprnfor: true
        tstress: true
      ELECTRONS:
        conv_thr: 4.0e-10
        electron_maxstep: 80
        mixing_beta: 0.4
      SYSTEM:
        degauss: 0.01
        ecutrho: 240.0
        ecutwfc: 30.0
        nosym: false
        occupations: smearing
        smearing: cold
    pseudos:
      Si: ''
structure: Si

Copy link
Contributor

@sphuber sphuber left a comment

Choose a reason for hiding this comment

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

Thanks @mbercx just two minor comments remaining. And then if you are still up for it, I would take you up on your suggestion to use YAML instead of JSON.

tests/conftest.py Outdated Show resolved Hide resolved
aiida/engine/processes/builder.py Outdated Show resolved Hide resolved
tests/engine/processes/test_builder.py Outdated Show resolved Hide resolved
tests/engine/processes/test_builder.py Outdated Show resolved Hide resolved
@mbercx mbercx requested a review from sphuber January 27, 2022 11:28
"""Pretty representation for in the IPython console and notebooks."""
return p.text(
f'Process class: {self._process_class.__name__}\n'
f'Inputs:\n{yaml.safe_dump(json.JSONDecoder().decode(PrettyEncoder().encode(self)))}'
Copy link
Contributor

Choose a reason for hiding this comment

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

Doing a full encode/decode cycle in JSON before encoding in YAML is a bit inefficient. Then again, writing serializers in YAML is a bit of a mess (like in aiida.orm.utils.serialize, and unfortunately we cannot even reuse that) and this should not be horribly costly, so let's leave this as is.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I know it's a bit of a messy workaround, but didn't know how to write the serializer in YAML to be honest. I timed the line with %%timeit and it only took a couple dozen nanoseconds, so should be ok. ^^

@sphuber sphuber merged commit 2b6b2e9 into aiidateam:develop Jan 27, 2022
@sphuber
Copy link
Contributor

sphuber commented Jan 27, 2022

Thanks a lot @mbercx !

@mbercx mbercx deleted the fix/print-process branch January 27, 2022 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants